diff --git a/.claude/skills/mdex-process-safety/SKILL.md b/.claude/skills/mdex-process-safety/SKILL.md new file mode 100644 index 0000000..e5167b6 --- /dev/null +++ b/.claude/skills/mdex-process-safety/SKILL.md @@ -0,0 +1,172 @@ +--- +name: mdex-process-safety +description: How to find, wait on, and stop processes on this machine without hanging or killing someone else's work. Read BEFORE writing any pgrep/pkill/ps lookup, before polling for a background job or deploy to finish, before stopping a bot fleet, simulator, or dev server, before running or scripting any icp CLI command (the identity rules), and before trusting a red test run. This machine runs many parallel Claude sessions plus long-lived MULTI/DEX fleets driving the LIVE subnet, so pattern-matching a process is never safe here. +metadata: + title: Process safety on a shared machine + category: Operations +--- + +# Process safety on a shared machine + +This checkout is worked on by **many Claude sessions at once**, on a machine that is +also running **long-lived bot fleets** — one against the local replica, one against the +cloud engine, and one driving the **live subnet at multidex.ai**. Several worktrees of +other projects run their own background jobs beside them. + +Two consequences, and both have already cost real time: + +- **A process pattern you write will match things you did not mean** — other sessions' + shells, other fleets, and *your own waiter*. +- **A wait that never ends looks exactly like work in progress.** Nobody notices for + twenty minutes. + +--- + +## 1. Never wait by `pgrep`. It matches your own shell. + +This hangs forever: + +```bash +until ! pgrep -f "cold_start.sh --mode full" >/dev/null; do sleep 5; done +``` + +The pattern string is **inside the waiting shell's own command line**, so `pgrep -f` +matches the waiter itself. The condition can never go false. `pgrep` excludes its own +PID, not its parent's, so this is not defended against. + +Three of these were spawned in one session on 2026-08-05. One was supposed to run the +integration suite after a deploy; it sat in the loop and the suite **never started**, +while a `pgrep -f run_all.sh` status check "confirmed" it was running — that check +matched the stuck waiter's command text, not a real process. + +**Instead:** + +- **Preferred — don't poll at all.** Launch with the Bash tool's `run_in_background` and + let the completion notification wake you. For "A then B", put both in *one* + backgrounded command: `bash a.sh && bash b.sh`. No waiter, nothing to self-match. +- **If you must poll a process, poll a PID you captured**, never a pattern: + ```bash + bash long_thing.sh & PID=$! + until ! kill -0 "$PID" 2>/dev/null; do sleep 5; done + ``` +- **If you must poll for a condition, poll a marker the job writes** — a file, a log + line — and make sure the marker can actually appear. A second waiter that session + polled for `"Cold start complete|✗|Error|failed"`; the script prints none of those + strings, so its exit condition could never fire either. Before arming a grep-based + wait, grep the *finished* log of a previous run for the pattern. + +--- + +## 2. Never `pkill -f` / pattern-kill. Kill by PID or by ancestry. + +The repo's incident trail records a pattern kill taking down the **live subnet fleet** +on 2026-07-23, 2026-07-28 and 2026-08-01. `pkill -f simulate_trading.sh` could not tell +a local simulator from the one driving multidex.ai, and a legacy fleet carried its +target only in `IC_ENV` — invisible to `ps`. + +The repo already has the correct architecture. **Use it, do not reinvent it:** + +| Need | Use | +|---|---| +| Start a fleet | `bash scripts/start_bots_.sh` | +| Stop a fleet | `bash scripts/stop_bots_.sh` | +| What is recorded | `.run/bots-.pid` — one supervisor PID per target | +| How stopping works | `mdx_kill_tree` in `scripts/lib/bots.sh` — walks `pgrep -P` (parent), never a name | + +`stop_bots_.sh` **deliberately does not fall back to a pattern search** when the +PID file is missing. It reports the unrecorded processes and stops. Preserve that. An +unrecorded fleet is a thing for a human to look at, not something to guess at. + +If you truly must stop something with no PID file: identify it with +`ps -eo pid,ppid,etime,command`, confirm which target it drives, and `kill` **that +numeric PID**. Never a pattern. Never in a loop. + +Before killing anything, check what else is running so you can prove you didn't touch it: + +```bash +ps -eo pid,ppid,etime,command | grep '[t]rading_simulation' +``` + +Other sessions' jobs are usually recognisable by a different parent shell and a +different worktree path in their command line. Leave them alone. + +--- + +## 3. Verify the effect, not the mechanism + +A guard that fails silently is worse than no guard, because the run still prints a +total and the total gets believed. + +`tests/run_all.sh`'s bot guard was inert for its whole life: it resolved the stopper via +`$(dirname "$0")/../scripts/...` while the script had already `cd`-ed into `tests/`, so +from the repo root it tested a path that does not exist, and the `if` had no `else`. It +worked when run from inside `tests/` and no-oped when run the normal way. It was read, +reviewed and *documented as fixed* before anyone watched it actually stop a fleet. The +suite came back **20 red**; the true number was lower and 7 of those tests were pure +simulator noise. + +So: + +- After a stop, assert the thing is **gone**: `kill -0 "$PID"` fails, PID file removed. +- After a start, assert it is **there** and recorded. +- In scripts, prefer `$SCRIPT_DIR` (absolute, computed once at the top) over + `$(dirname "$0")` — `$0` is the invocation path and breaks the moment anything `cd`s. +- Gate on `-f`, not `-x`, for a helper you invoke as `bash `; it needs no + executable bit, and `-x` lets a stray `chmod` silently disable the guard. +- Any guard whose failure mode is "do nothing" needs an `else` that says so out loud. + +--- + +## 4. Before you trust a red test run + +Integration results here are only meaningful on a **quiet, freshly seeded** venue. Two +independent things corrupt them, and both look like code regressions: + +1. **A bot fleet trading underneath the assertions.** Confirm the suite printed + `Stopping the local simulator…` and that `.run/bots-local.pid` is gone. Symptoms: + value-conservation drift, moving balances, `arb "idle"`, anchors that will not stay put. +2. **A venue an earlier run already wiped.** `tests/test_state_reset.sh` calls + `resetExchange` near the end of the suite, so a *second* run starts against an empty + exchange — no AMM pools, no vault. The suite is **not idempotent across runs**. + Symptoms: zeros where money should be (`vaultLPSupply = 0`, balances `0`, nothing + staged), and empty `got:` values. + +Cheap check before believing anything: + +```bash +echo y | icp canister call backend getAmmPools '()' --query --identity anonymous +``` + +`(vec {})` means the venue is wiped — reseed with `bash scripts/cold_start.sh --mode full` +and re-run. Otherwise you are reading noise. + +--- + +## 5. Never use the CLI's default identity. Every `icp` command carries `--identity`. + +The default identity lives in the machine-global icp store +(`~/Library/Application Support/org.dfinity.icp-cli/identity/identity_defaults.json`) +and is state **this repo does not own**. Other sessions and connected MCP connectors +(the Open SaaS connector holds its own principals) move it whenever they like — on +2026-08-06 it changed hands twice within hours (`opensaas`, then `opensaas-engine`) +while three workstreams shared the machine — including between your +`icp identity default` call and the command a line later. + +When that happens the command runs as whoever won the race. A deploy runs as a +non-controller and every canister fails with `IC0512 ... Only controllers ... can call +ic00 method update_settings` — which reads like a project permissions bug and is not +one. A bare `icp canister call` silently acts as the wrong principal — on the live +subnet that misattributes actions on the public, principal-attributed tape. + +Two rules, no exceptions: + +- **Every `icp` invocation that acts as an identity carries `--identity `** — + `icp deploy`, `icp canister call`, `icp canister status`, scripts, one-off shell + lines, all of it. `--identity` scopes the identity to that one command and cannot + be raced. +- **Never run `icp identity default `.** Reading the default is unreliable + precisely because other actors write it; do not become one of them. A workstream + that genuinely needs its own ambient identity gets an isolated store via + `ICP_HOME=`, never the shared default. + +Locally the controller is `anonymous`; admin scripts use `alice`. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..3e4878d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,34 @@ +# Dependabot configuration — https://docs.github.com/code-security/dependabot +# Security updates (auto-PRs for known CVEs) are enabled separately in repo +# Settings and do NOT require this file. This file adds *scheduled version +# updates*: weekly PRs bumping deps to their latest versions, CVE or not. +# +# Note: mops (Motoko: core/json/sha2) has no Dependabot ecosystem, so those +# are not covered here — track them manually via `mops outdated`. +version: 2 +updates: + # Frontend / build tooling (npm) + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 5 + commit-message: + prefix: "chore(deps)" + groups: + # Bundle dev/build tooling into one PR instead of several + dev-tooling: + patterns: + - "vite" + - "postcss" + - "@types/*" + + # GitHub Actions (takes effect once CI workflows are added) + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + commit-message: + prefix: "chore(ci)" diff --git a/.gitignore b/.gitignore index 7bef363..9021abf 100644 --- a/.gitignore +++ b/.gitignore @@ -5,13 +5,23 @@ dist/ # Skills are a machine-synced mirror of skills.internetcomputer.org, kept # current by the SessionStart hook (.claude/sync-ic-skills.sh) — never commit. -.claude/skills/ +# Glob the CONTENTS rather than the directory so the exceptions below can be +# re-included: git does not descend into an excluded directory, so `!` on a +# path underneath `.claude/skills/` would never be consulted. +.claude/skills/* +# ...except skills this repo OWNS. These are ours, not mirrored, and the sync +# script only prunes what it installed (.claude/skills/.ic-managed.json), so +# they survive a sync. Keep this list explicit — a blanket un-ignore would +# start committing the mirror. +!.claude/skills/mdex-process-safety/ # Per-developer Claude Code local overrides (machine paths, personal allowlist). .claude/settings.local.json -# moc build cache + per-machine moc-resolution marker (not source). -.mops/.build/ -.mops/moc-* +# Installed Motoko packages, the moc build cache, and the per-machine +# moc-resolution marker — none of it is source. `mops install` reproduces the +# whole tree from mops.toml + mops.lock, and the lockfile carries a SHA-256 per +# file, so integrity is verified rather than vendored. +.mops/ # icp-cli machine/deploy state (canister-id mappings, local network data). # .icp/data/ (mainnet canister-id mappings) is COMMITTED — it is the link @@ -19,6 +29,9 @@ dist/ # deploy to CREATE new canisters instead of upgrading multidex.ai's (skill: # icp-cli pitfall 5). Only the ephemeral cache (incl. the local replica's # mapping, which changes on every network reset) stays ignored. +# 2026-08-06: the legacy shared `ic.ids.json` mapping was DELETED, safely — +# it held a strict subset of subnet.ids.json (identical live ids). Stacks are +# addressed only via their declared environments (-e engine | -e subnet). .icp/cache/ # Cloud-engine / subnet deploy config (machine/target-specific) diff --git a/.icp/data/mappings/ic.ids.json b/.icp/data/mappings/ic.ids.json deleted file mode 100644 index 38b7804..0000000 --- a/.icp/data/mappings/ic.ids.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "backend": "hmxr2-pqaaa-aaabq-qaaaa-cai", - "bridge": "hlwxo-ciaaa-aaabq-qaaaq-cai", - "frontend": "hcv4s-uaaaa-aaabq-qaaba-cai" -} diff --git a/.mops/base@0.11.1/LICENSE b/.mops/base@0.11.1/LICENSE deleted file mode 100644 index d5dadbd..0000000 --- a/.mops/base@0.11.1/LICENSE +++ /dev/null @@ -1,208 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, and - distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by the - copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all other - entities that control, are controlled by, or are under common control with - that entity. For the purposes of this definition, "control" means (i) the - power, direct or indirect, to cause the direction or management of such - entity, whether by contract or otherwise, or (ii) ownership of fifty percent - (50%) or more of the outstanding shares, or (iii) beneficial ownership of - such entity. - - "You" (or "Your") shall mean an individual or Legal Entity exercising - permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation source, and - configuration files. - - "Object" form shall mean any form resulting from mechanical transformation - or translation of a Source form, including but not limited to compiled - object code, generated documentation, and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or Object form, - made available under the License, as indicated by a copyright notice that is - included in or attached to the work (an example is provided in the Appendix - below). - - "Derivative Works" shall mean any work, whether in Source or Object form, - that is based on (or derived from) the Work and for which the editorial - revisions, annotations, elaborations, or other modifications represent, as a - whole, an original work of authorship. For the purposes of this License, - Derivative Works shall not include works that remain separable from, or - merely link (or bind by name) to the interfaces of, the Work and Derivative - Works thereof. - - "Contribution" shall mean any work of authorship, including the original - version of the Work and any modifications or additions to that Work or - Derivative Works thereof, that is intentionally submitted to Licensor for - inclusion in the Work by the copyright owner or by an individual or Legal - Entity authorized to submit on behalf of the copyright owner. For the - purposes of this definition, "submitted" means any form of electronic, - verbal, or written communication sent to the Licensor or its - representatives, including but not limited to communication on electronic - mailing lists, source code control systems, and issue tracking systems that - are managed by, or on behalf of, the Licensor for the purpose of discussing - and improving the Work, but excluding communication that is conspicuously - marked or otherwise designated in writing by the copyright owner as "Not a - Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity on - behalf of whom a Contribution has been received by Licensor and subsequently - incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this - License, each Contributor hereby grants to You a perpetual, worldwide, - non-exclusive, no-charge, royalty-free, irrevocable copyright license to - reproduce, prepare Derivative Works of, publicly display, publicly perform, - sublicense, and distribute the Work and such Derivative Works in Source or - Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this - License, each Contributor hereby grants to You a perpetual, worldwide, - non-exclusive, no-charge, royalty-free, irrevocable (except as stated in - this section) patent license to make, have made, use, offer to sell, sell, - import, and otherwise transfer the Work, where such license applies only to - those patent claims licensable by such Contributor that are necessarily - infringed by their Contribution(s) alone or by combination of their - Contribution(s) with the Work to which such Contribution(s) was submitted. - If You institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work or a - Contribution incorporated within the Work constitutes direct or contributory - patent infringement, then any patent licenses granted to You under this - License for that Work shall terminate as of the date such litigation is - filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or - Derivative Works thereof in any medium, with or without modifications, and - in Source or Object form, provided that You meet the following conditions: - - a. You must give any other recipients of the Work or Derivative Works a - copy of this License; and - - b. You must cause any modified files to carry prominent notices stating - that You changed the files; and - - c. You must retain, in the Source form of any Derivative Works that You - distribute, all copyright, patent, trademark, and attribution notices - from the Source form of the Work, excluding those notices that do not - pertain to any part of the Derivative Works; and - - d. If the Work includes a "NOTICE" text file as part of its distribution, - then any Derivative Works that You distribute must include a readable - copy of the attribution notices contained within such NOTICE file, - excluding those notices that do not pertain to any part of the Derivative - Works, in at least one of the following places: within a NOTICE text file - distributed as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, within a - display generated by the Derivative Works, if and wherever such - third-party notices normally appear. The contents of the NOTICE file are - for informational purposes only and do not modify the License. You may - add Your own attribution notices within Derivative Works that You - distribute, alongside or as an addendum to the NOTICE text from the Work, - provided that such additional attribution notices cannot be construed as - modifying the License. - - You may add Your own copyright statement to Your modifications and may - provide additional or different license terms and conditions for use, - reproduction, or distribution of Your modifications, or for any such - Derivative Works as a whole, provided Your use, reproduction, and - distribution of the Work otherwise complies with the conditions stated in - this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any - Contribution intentionally submitted for inclusion in the Work by You to the - Licensor shall be under the terms and conditions of this License, without - any additional terms or conditions. Notwithstanding the above, nothing - herein shall supersede or modify the terms of any separate license agreement - you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, - trademarks, service marks, or product names of the Licensor, except as - required for reasonable and customary use in describing the origin of the - Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in - writing, Licensor provides the Work (and each Contributor provides its - Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied, including, without limitation, any - warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or - FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining - the appropriateness of using or redistributing the Work and assume any risks - associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in - tort (including negligence), contract, or otherwise, unless required by - applicable law (such as deliberate and grossly negligent acts) or agreed to - in writing, shall any Contributor be liable to You for damages, including - any direct, indirect, special, incidental, or consequential damages of any - character arising as a result of this License or out of the use or inability - to use the Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all other - commercial damages or losses), even if such Contributor has been advised of - the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or - Derivative Works thereof, You may choose to offer, and charge a fee for, - acceptance of support, warranty, indemnity, or other liability obligations - and/or rights consistent with this License. However, in accepting such - obligations, You may act only on Your own behalf and on Your sole - responsibility, not on behalf of any other Contributor, and only if You - agree to indemnify, defend, and hold each Contributor harmless for any - liability incurred by, or claims asserted against, such Contributor by - reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -LLVM EXCEPTIONS TO THE APACHE 2.0 LICENSE - -As an exception, if, as a result of your compiling your source code, portions -of this Software are embedded into an Object form of such source code, you may -redistribute such embedded portions in such Object form without complying with -the conditions of Sections 4(a), 4(b) and 4(d) of the License. - -In addition, if you combine or link compiled forms of this Software with -software that is licensed under the GPLv2 ("Combined Software") and if a court -of competent jurisdiction determines that the patent provision (Section 3), the -indemnity provision (Section 9) or other Section of the License conflicts with -the conditions of the GPLv2, you may retroactively and prospectively choose to -deem waived or otherwise exclude such Section(s) of the License, but only in -their entirety and only with respect to the Combined Software. - -END OF LLVM EXCEPTIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification -within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. - -END OF APPENDIX diff --git a/.mops/base@0.11.1/NOTICE b/.mops/base@0.11.1/NOTICE deleted file mode 100644 index 477d573..0000000 --- a/.mops/base@0.11.1/NOTICE +++ /dev/null @@ -1,12 +0,0 @@ -Copyright 2020 DFINITY Stiftung - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. diff --git a/.mops/base@0.11.1/README.md b/.mops/base@0.11.1/README.md deleted file mode 100644 index e0584f4..0000000 --- a/.mops/base@0.11.1/README.md +++ /dev/null @@ -1,104 +0,0 @@ -The Motoko base library -======================= - -This repository contains the Motoko base library. It is intended to be used with the [`moc` compiler](https://github.com/dfinity/motoko) (and tools that wrap it, like `dfx`). - -Usage ------ - -If you are installing Motoko through the DFINITY SDK releases, then this base -library is already included. - -If you build your project using the [Mops package manager], run the following command to add the base package to your project: - -```sh -mops add base -``` - -If you build your project using the [Vessel package manager] your package-set most likely already includes base, but if it doesn't or you want to override its version, add an entry like so to your `package-set.dhall`: - -``` - { - name = "base", - repo = "https://github.com/dfinity/motoko-base", - version = "master", - dependencies = [] : List Text - } -``` - -The package _name_ `"base"` appears when importing its modules in Motoko (e.g., `import "mo:base/Nat"`). The _repo_ may either be your local clone path, or this public repository url, as above. The _version_ can be any git branch or tag name (such as `version = "moc-0.8.4"`). There are no dependencies. See the [Vessel package manager] docs for more details. - -[Mops package manager]: https://mops.one - -[Vessel package manager]: https://github.com/dfinity/vessel - -Building & Testing ------------------- - -Run the following commands to configure your local development branch: - -```sh -# First-time setup -git clone https://github.com/dfinity/motoko-base -cd motoko-base -npm install - -# Run tests -npm test - -# Run all tests in wasi mode -npm test -- --mode wasi - -# Run formatter -npm run prettier:format -``` - -**Note**: -- If you are using `npm test` to run the tests: - - You don't need to install any additional dependencies. - - The test runner will automatically download the `moc` and `wasmtime` versions specified in `mops.toml` in the `[toolchain]` section. - -- If you are using `Makefile` to run the tests: - - The test runner will automatically detect the `moc` compiler from your system path or `dfx` installation. - - - Running the tests locally also requires [Wasmtime](https://wasmtime.dev/) and [Vessel](https://github.com/dfinity/vessel) to be installed on your system. - -Run only specific test files: -```sh -npm test -``` - -For example `npm test list` will run `List.test.mo` and `AssocList.test.mo` test files. - -Run tests in watch mode: -```sh -npm test -- --watch - -# useful to combine with filter when writing tests -npm test array -- --watch -``` - -Documentation -------------- - -The documentation can be generated in `doc/` by running - -```sh -./make_docs.sh -``` - -which creates `_out/html/index.html`. - -The `next-moc` branch ---------------------- - -The `next-moc` branch contains changes that make base compatible with the -in-development version of `moc`. This repository's public CI does _not_ run -on that branch. - -External contributions are best made against `master`. - -Contributing ------------- - -Please read the [Interface Design Guide for Motoko Base Library](doc/design.md) before making a pull request. diff --git a/.mops/base@0.11.1/mops.toml b/.mops/base@0.11.1/mops.toml deleted file mode 100644 index 2bec8a1..0000000 --- a/.mops/base@0.11.1/mops.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "base" -version = "0.11.1" -description = "The Motoko base library" -repository = "https://github.com/dfinity/motoko-base" -keywords = [ "base" ] -license = "Apache-2.0" - -[dev-dependencies] -matchers = "https://github.com/kritzcreek/motoko-matchers#v1.3.0@3dac8a071b69e4e651b25a7d9683fe831eb7cffd" - -[toolchain] -moc = "0.11.1" -wasmtime = "17.0.0" diff --git a/.mops/base@0.11.1/src/Array.mo b/.mops/base@0.11.1/src/Array.mo deleted file mode 100644 index 8d262b4..0000000 --- a/.mops/base@0.11.1/src/Array.mo +++ /dev/null @@ -1,865 +0,0 @@ -/// Provides extended utility functions on Arrays. -/// -/// Note the difference between mutable and non-mutable arrays below. -/// -/// WARNING: If you are looking for a list that can grow and shrink in size, -/// it is recommended you use either the Buffer class or the List class for -/// those purposes. Arrays must be created with a fixed size. -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Array "mo:base/Array"; -/// ``` - -import I "IterType"; -import Option "Option"; -import Order "Order"; -import Prim "mo:⛔"; -import Result "Result"; - -module { - /// Create a mutable array with `size` copies of the initial value. - /// - /// ```motoko include=import - /// let array = Array.init(4, 2); - /// ``` - /// - /// Runtime: O(size) - /// Space: O(size) - public func init(size : Nat, initValue : X) : [var X] = Prim.Array_init(size, initValue); - - /// Create an immutable array of size `size`. Each element at index i - /// is created by applying `generator` to i. - /// - /// ```motoko include=import - /// let array : [Nat] = Array.tabulate(4, func i = i * 2); - /// ``` - /// - /// Runtime: O(size) - /// Space: O(size) - /// - /// *Runtime and space assumes that `generator` runs in O(1) time and space. - public func tabulate(size : Nat, generator : Nat -> X) : [X] = Prim.Array_tabulate(size, generator); - - /// Create a mutable array of size `size`. Each element at index i - /// is created by applying `generator` to i. - /// - /// ```motoko include=import - /// let array : [var Nat] = Array.tabulateVar(4, func i = i * 2); - /// array[2] := 0; - /// array - /// ``` - /// - /// Runtime: O(size) - /// Space: O(size) - /// - /// *Runtime and space assumes that `generator` runs in O(1) time and space. - public func tabulateVar(size : Nat, generator : Nat -> X) : [var X] { - // FIXME add this as a primitive in the RTS - if (size == 0) { return [var] }; - let array = Prim.Array_init(size, generator 0); - var i = 1; - while (i < size) { - array[i] := generator i; - i += 1 - }; - array - }; - - /// Transforms a mutable array into an immutable array. - /// - /// ```motoko include=import - /// - /// let varArray = [var 0, 1, 2]; - /// varArray[2] := 3; - /// let array = Array.freeze(varArray); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func freeze(varArray : [var X]) : [X] = Prim.Array_tabulate(varArray.size(), func i = varArray[i]); - - /// Transforms an immutable array into a mutable array. - /// - /// ```motoko include=import - /// - /// let array = [0, 1, 2]; - /// let varArray = Array.thaw(array); - /// varArray[2] := 3; - /// varArray - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func thaw(array : [A]) : [var A] { - let size = array.size(); - if (size == 0) { - return [var] - }; - let newArray = Prim.Array_init(size, array[0]); - var i = 0; - while (i < size) { - newArray[i] := array[i]; - i += 1 - }; - newArray - }; - - /// Tests if two arrays contain equal values (i.e. they represent the same - /// list of elements). Uses `equal` to compare elements in the arrays. - /// - /// ```motoko include=import - /// // Use the equal function from the Nat module to compare Nats - /// import {equal} "mo:base/Nat"; - /// - /// let array1 = [0, 1, 2, 3]; - /// let array2 = [0, 1, 2, 3]; - /// Array.equal(array1, array2, equal) - /// ``` - /// - /// Runtime: O(size1 + size2) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func equal(array1 : [X], array2 : [X], equal : (X, X) -> Bool) : Bool { - let size1 = array1.size(); - let size2 = array2.size(); - if (size1 != size2) { - return false - }; - var i = 0; - while (i < size1) { - if (not equal(array1[i], array2[i])) { - return false - }; - i += 1 - }; - return true - }; - - /// Returns the first value in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let array = [1, 9, 4, 8]; - /// Array.find(array, func x = x > 8) - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func find(array : [X], predicate : X -> Bool) : ?X { - for (element in array.vals()) { - if (predicate element) { - return ?element - } - }; - return null - }; - - /// Create a new array by appending the values of `array1` and `array2`. - /// @deprecated `Array.append` copies its arguments and has linear complexity; - /// when used in a loop, consider using a `Buffer`, and `Buffer.append`, instead. - /// - /// ```motoko include=import - /// let array1 = [1, 2, 3]; - /// let array2 = [4, 5, 6]; - /// Array.append(array1, array2) - /// ``` - /// Runtime: O(size1 + size2) - /// - /// Space: O(size1 + size2) - public func append(array1 : [X], array2 : [X]) : [X] { - let size1 = array1.size(); - let size2 = array2.size(); - Prim.Array_tabulate( - size1 + size2, - func i { - if (i < size1) { - array1[i] - } else { - array2[i - size1] - } - } - ) - }; - - // FIXME this example stack overflows. Should test with new implementation of sortInPlace - /// Sorts the elements in the array according to `compare`. - /// Sort is deterministic and stable. - /// - /// ```motoko include=import - /// import Nat "mo:base/Nat"; - /// - /// let array = [4, 2, 6]; - /// Array.sort(array, Nat.compare) - /// ``` - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sort(array : [X], compare : (X, X) -> Order.Order) : [X] { - let temp : [var X] = thaw(array); - sortInPlace(temp, compare); - freeze(temp) - }; - - /// Sorts the elements in the array, __in place__, according to `compare`. - /// Sort is deterministic, stable, and in-place. - /// - /// ```motoko include=import - /// - /// import {compare} "mo:base/Nat"; - /// - /// let array = [var 4, 2, 6]; - /// Array.sortInPlace(array, compare); - /// array - /// ``` - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sortInPlace(array : [var X], compare : (X, X) -> Order.Order) { - // Stable merge sort in a bottom-up iterative style. Same algorithm as the sort in Buffer. - let size = array.size(); - if (size == 0) { - return - }; - let scratchSpace = Prim.Array_init(size, array[0]); - - let sizeDec = size - 1 : Nat; - var currSize = 1; // current size of the subarrays being merged - // when the current size == size, the array has been merged into a single sorted array - while (currSize < size) { - var leftStart = 0; // selects the current left subarray being merged - while (leftStart < sizeDec) { - let mid : Nat = if (leftStart + currSize - 1 : Nat < sizeDec) { - leftStart + currSize - 1 - } else { sizeDec }; - let rightEnd : Nat = if (leftStart + (2 * currSize) - 1 : Nat < sizeDec) { - leftStart + (2 * currSize) - 1 - } else { sizeDec }; - - // Merge subarrays elements[leftStart...mid] and elements[mid+1...rightEnd] - var left = leftStart; - var right = mid + 1; - var nextSorted = leftStart; - while (left < mid + 1 and right < rightEnd + 1) { - let leftElement = array[left]; - let rightElement = array[right]; - switch (compare(leftElement, rightElement)) { - case (#less or #equal) { - scratchSpace[nextSorted] := leftElement; - left += 1 - }; - case (#greater) { - scratchSpace[nextSorted] := rightElement; - right += 1 - } - }; - nextSorted += 1 - }; - while (left < mid + 1) { - scratchSpace[nextSorted] := array[left]; - nextSorted += 1; - left += 1 - }; - while (right < rightEnd + 1) { - scratchSpace[nextSorted] := array[right]; - nextSorted += 1; - right += 1 - }; - - // Copy over merged elements - var i = leftStart; - while (i < rightEnd + 1) { - array[i] := scratchSpace[i]; - i += 1 - }; - - leftStart += 2 * currSize - }; - currSize *= 2 - } - }; - - /// Creates a new array by reversing the order of elements in `array`. - /// - /// ```motoko include=import - /// - /// let array = [10, 11, 12]; - /// - /// Array.reverse(array) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func reverse(array : [X]) : [X] { - let size = array.size(); - Prim.Array_tabulate(size, func i = array[size - i - 1]) - }; - - /// Creates a new array by applying `f` to each element in `array`. `f` "maps" - /// each element it is applied to of type `X` to an element of type `Y`. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// - /// let array = [0, 1, 2, 3]; - /// Array.map(array, func x = x * 3) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func map(array : [X], f : X -> Y) : [Y] = Prim.Array_tabulate(array.size(), func i = f(array[i])); - - /// Creates a new array by applying `predicate` to every element - /// in `array`, retaining the elements for which `predicate` returns true. - /// - /// ```motoko include=import - /// let array = [4, 2, 6, 1, 5]; - /// let evenElements = Array.filter(array, func x = x % 2 == 0); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func filter(array : [X], predicate : X -> Bool) : [X] { - var count = 0; - let keep = Prim.Array_tabulate( - array.size(), - func i { - if (predicate(array[i])) { - count += 1; - true - } else { - false - } - } - ); - var nextKeep = 0; - Prim.Array_tabulate( - count, - func _ { - while (not keep[nextKeep]) { - nextKeep += 1 - }; - nextKeep += 1; - array[nextKeep - 1] - } - ) - }; - - // FIXME the arguments ordering to the higher order function are flipped - // between this and the buffer class - // probably can't avoid breaking changes at some point - /// Creates a new array by applying `f` to each element in `array` and its index. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// - /// let array = [10, 10, 10, 10]; - /// Array.mapEntries(array, func (x, i) = i * x) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapEntries(array : [X], f : (X, Nat) -> Y) : [Y] = Prim.Array_tabulate(array.size(), func i = f(array[i], i)); - - /// Creates a new array by applying `f` to each element in `array`, - /// and keeping all non-null elements. The ordering is retained. - /// - /// ```motoko include=import - /// import {toText} "mo:base/Nat"; - /// - /// let array = [4, 2, 0, 1]; - /// let newArray = - /// Array.mapFilter( // mapping from Nat to Text values - /// array, - /// func x = if (x == 0) { null } else { ?toText(100 / x) } // can't divide by 0, so return null - /// ); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapFilter(array : [X], f : X -> ?Y) : [Y] { - var count = 0; - let options = Prim.Array_tabulate( - array.size(), - func i { - let result = f(array[i]); - switch (result) { - case (?element) { - count += 1; - result - }; - case null { - null - } - } - } - ); - - var nextSome = 0; - Prim.Array_tabulate( - count, - func _ { - while (Option.isNull(options[nextSome])) { - nextSome += 1 - }; - nextSome += 1; - switch (options[nextSome - 1]) { - case (?element) element; - case null { - Prim.trap "Malformed array in mapFilter" - } - } - } - ) - }; - - /// Creates a new array by applying `f` to each element in `array`. - /// If any invocation of `f` produces an `#err`, returns an `#err`. Otherwise - /// returns an `#ok` containing the new array. - /// - /// ```motoko include=import - /// let array = [4, 3, 2, 1, 0]; - /// // divide 100 by every element in the array - /// Array.mapResult(array, func x { - /// if (x > 0) { - /// #ok(100 / x) - /// } else { - /// #err "Cannot divide by zero" - /// } - /// }) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapResult(array : [X], f : X -> Result.Result) : Result.Result<[Y], E> { - let size = array.size(); - - var error : ?Result.Result<[Y], E> = null; - let results = Prim.Array_tabulate( - size, - func i { - switch (f(array[i])) { - case (#ok element) { - ?element - }; - case (#err e) { - switch (error) { - case null { - // only take the first error - error := ?(#err e) - }; - case _ {} - }; - null - } - } - } - ); - - switch error { - case null { - // unpack the option - #ok( - map( - results, - func element { - switch element { - case (?element) { - element - }; - case null { - Prim.trap "Malformed array in mapResults" - } - } - } - ) - ) - }; - case (?error) { - error - } - } - }; - - /// Creates a new array by applying `k` to each element in `array`, - /// and concatenating the resulting arrays in order. This operation - /// is similar to what in other functional languages is known as monadic bind. - /// - /// ```motoko include=import - /// import Nat "mo:base/Nat"; - /// - /// let array = [1, 2, 3, 4]; - /// Array.chain(array, func x = [x, -x]) - /// - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `k` runs in O(1) time and space. - public func chain(array : [X], k : X -> [Y]) : [Y] { - var flatSize = 0; - let arrays = Prim.Array_tabulate<[Y]>( - array.size(), - func i { - let subArray = k(array[i]); - flatSize += subArray.size(); - subArray - } - ); - - // could replace with a call to flatten, - // but it would require an extra pass (to compute `flatSize`) - var outer = 0; - var inner = 0; - Prim.Array_tabulate( - flatSize, - func _ { - while (inner == arrays[outer].size()) { - inner := 0; - outer += 1 - }; - let element = arrays[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// ```motoko include=import - /// import {add} "mo:base/Nat"; - /// - /// let array = [4, 2, 0, 1]; - /// let sum = - /// Array.foldLeft( - /// array, - /// 0, // start the sum at 0 - /// func(sumSoFar, x) = sumSoFar + x // this entire function can be replaced with `add`! - /// ); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(array : [X], base : A, combine : (A, X) -> A) : A { - var accumulation = base; - - for (element in array.vals()) { - accumulation := combine(accumulation, element) - }; - - accumulation - }; - - // FIXME the type arguments are reverse order from Buffer - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// ```motoko include=import - /// import {toText} "mo:base/Nat"; - /// - /// let array = [1, 9, 4, 8]; - /// let bookTitle = Array.foldRight(array, "", func(x, acc) = toText(x) # acc); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldRight(array : [X], base : A, combine : (X, A) -> A) : A { - var accumulation = base; - let size = array.size(); - - var i = size; - while (i > 0) { - i -= 1; - accumulation := combine(array[i], accumulation) - }; - - accumulation - }; - - /// Flattens the array of arrays into a single array. Retains the original - /// ordering of the elements. - /// - /// ```motoko include=import - /// - /// let arrays = [[0, 1, 2], [2, 3], [], [4]]; - /// Array.flatten(arrays) - /// ``` - /// - /// Runtime: O(number of elements in array) - /// - /// Space: O(number of elements in array) - public func flatten(arrays : [[X]]) : [X] { - var flatSize = 0; - for (subArray in arrays.vals()) { - flatSize += subArray.size() - }; - - var outer = 0; - var inner = 0; - Prim.Array_tabulate( - flatSize, - func _ { - while (inner == arrays[outer].size()) { - inner := 0; - outer += 1 - }; - let element = arrays[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Create an array containing a single value. - /// - /// ```motoko include=import - /// Array.make(2) - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func make(element : X) : [X] = [element]; - - /// Returns an Iterator (`Iter`) over the elements of `array`. - /// Iterator provides a single method `next()`, which returns - /// elements in order, or `null` when out of elements to iterate over. - /// - /// NOTE: You can also use `array.vals()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// - /// let array = [10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.vals()) { - /// sum += element; - /// }; - /// sum - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func vals(array : [X]) : I.Iter = array.vals(); - - /// Returns an Iterator (`Iter`) over the indices of `array`. - /// Iterator provides a single method `next()`, which returns - /// indices in order, or `null` when out of index to iterate over. - /// - /// NOTE: You can also use `array.keys()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// - /// let array = [10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.keys()) { - /// sum += element; - /// }; - /// sum - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func keys(array : [X]) : I.Iter = array.keys(); - - /// Returns the size of `array`. - /// - /// NOTE: You can also use `array.size()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// - /// let array = [10, 11, 12]; - /// let size = Array.size(array); - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func size(array : [X]) : Nat = array.size(); - - /// Returns a new subarray from the given array provided the start index and length of elements in the subarray - /// - /// Limitations: Traps if the start index + length is greater than the size of the array - /// - /// ```motoko include=import - /// - /// let array = [1,2,3,4,5]; - /// let subArray = Array.subArray(array, 2, 3); - /// ``` - /// Runtime: O(length); - /// Space: O(length); - public func subArray(array : [X], start : Nat, length : Nat) : [X] { - if (start + length > array.size()) { Prim.trap("Array.subArray") }; - tabulate( - length, - func(i) { - array[start + i] - } - ) - }; - - /// Returns the index of the first `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:base/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.indexOf('c', array, Char.equal) == ?0; - /// assert Array.indexOf('f', array, Char.equal) == ?2; - /// assert Array.indexOf('g', array, Char.equal) == null; - /// ``` - /// - /// Runtime: O(array.size()); - /// Space: O(1); - public func indexOf(element : X, array : [X], equal : (X, X) -> Bool) : ?Nat = nextIndexOf(element, array, 0, equal); - - /// Returns the index of the next occurence of `element` in the `array` starting from the `from` index (inclusive). - /// - /// ```motoko include=import - /// import Char "mo:base/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.nextIndexOf('c', array, 0, Char.equal) == ?0; - /// assert Array.nextIndexOf('f', array, 0, Char.equal) == ?2; - /// assert Array.nextIndexOf('f', array, 2, Char.equal) == ?2; - /// assert Array.nextIndexOf('f', array, 3, Char.equal) == ?3; - /// assert Array.nextIndexOf('f', array, 4, Char.equal) == null; - /// ``` - /// - /// Runtime: O(array.size()); - /// Space: O(1); - public func nextIndexOf(element : X, array : [X], fromInclusive : Nat, equal : (X, X) -> Bool) : ?Nat { - var i = fromInclusive; - let n = array.size(); - while (i < n) { - if (equal(array[i], element)) { - return ?i - } else { - i += 1 - } - }; - null - }; - - /// Returns the index of the last `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:base/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.lastIndexOf('c', array, Char.equal) == ?0; - /// assert Array.lastIndexOf('f', array, Char.equal) == ?3; - /// assert Array.lastIndexOf('e', array, Char.equal) == ?5; - /// assert Array.lastIndexOf('g', array, Char.equal) == null; - /// ``` - /// - /// Runtime: O(array.size()); - /// Space: O(1); - public func lastIndexOf(element : X, array : [X], equal : (X, X) -> Bool) : ?Nat = prevIndexOf(element, array, array.size(), equal); - - /// Returns the index of the previous occurance of `element` in the `array` starting from the `from` index (exclusive). - /// - /// ```motoko include=import - /// import Char "mo:base/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.prevIndexOf('c', array, array.size(), Char.equal) == ?0; - /// assert Array.prevIndexOf('e', array, array.size(), Char.equal) == ?5; - /// assert Array.prevIndexOf('e', array, 5, Char.equal) == ?4; - /// assert Array.prevIndexOf('e', array, 4, Char.equal) == null; - /// ``` - /// - /// Runtime: O(array.size()); - /// Space: O(1); - public func prevIndexOf(element : T, array : [T], fromExclusive : Nat, equal : (T, T) -> Bool) : ?Nat { - var i = fromExclusive; - while (i > 0) { - i -= 1; - if (equal(array[i], element)) { - return ?i - } - }; - null - }; - - /// Returns an iterator over a slice of the given array. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4, 5]; - /// let s = Array.slice(array, 3, array.size()); - /// assert s.next() == ?4; - /// assert s.next() == ?5; - /// assert s.next() == null; - /// - /// let s = Array.slice(array, 0, 0); - /// assert s.next() == null; - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func slice(array : [X], fromInclusive : Nat, toExclusive : Nat) : I.Iter = object { - var i = fromInclusive; - - public func next() : ?X { - if (i >= toExclusive) { - return null - }; - let result = array[i]; - i += 1; - return ?result - } - }; - - /// Returns a new subarray of given length from the beginning or end of the given array - /// - /// Returns the entire array if the length is greater than the size of the array - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4, 5]; - /// assert Array.take(array, 2) == [1, 2]; - /// assert Array.take(array, -2) == [4, 5]; - /// assert Array.take(array, 10) == [1, 2, 3, 4, 5]; - /// assert Array.take(array, -99) == [1, 2, 3, 4, 5]; - /// ``` - /// Runtime: O(length); - /// Space: O(length); - public func take(array : [T], length : Int) : [T] { - let len = Prim.abs(length); - let size = array.size(); - let resSize = if (len < size) { len } else { size }; - let start : Nat = if (length > 0) 0 else size - resSize; - subArray(array, start, resSize) - } -} diff --git a/.mops/base@0.11.1/src/AssocList.mo b/.mops/base@0.11.1/src/AssocList.mo deleted file mode 100644 index 219f40d..0000000 --- a/.mops/base@0.11.1/src/AssocList.mo +++ /dev/null @@ -1,402 +0,0 @@ -/// Map implemented as a linked-list of key-value pairs ("Associations"). -/// -/// NOTE: This map implementation is mainly used as underlying buckets for other map -/// structures. Thus, other map implementations are easier to use in most cases. - -import List "List"; - -module { - /// Import from the base library to use this module. - /// - /// ```motoko name=import - /// import AssocList "mo:base/AssocList"; - /// import List "mo:base/List"; - /// import Nat "mo:base/Nat"; - /// - /// type AssocList = AssocList.AssocList; - /// ``` - /// - /// Initialize an empty map using an empty list. - /// ```motoko name=initialize include=import - /// var map : AssocList = List.nil(); // Empty list as an empty map - /// map := null; // Alternative: null as empty list. - /// map - /// ``` - public type AssocList = List.List<(K, V)>; - - /// Find the value associated with key `key`, or `null` if no such key exists. - /// Compares keys using the provided function `equal`. - /// - /// Example: - /// ```motoko include=import,initialize - /// // Create map = [(0, 10), (1, 11), (2, 12)] - /// map := AssocList.replace(map, 0, Nat.equal, ?10).0; - /// map := AssocList.replace(map, 1, Nat.equal, ?11).0; - /// map := AssocList.replace(map, 2, Nat.equal, ?12).0; - /// - /// // Find value associated with key 1 - /// AssocList.find(map, 1, Nat.equal) - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func find( - map : AssocList, - key : K, - equal : (K, K) -> Bool - ) : ?V { - switch (map) { - case (?((hd_k, hd_v), tl)) { - if (equal(key, hd_k)) { - ?hd_v - } else { - find(tl, key, equal) - } - }; - case (null) { null } - } - }; - - /// Maps `key` to `value` in `map`, and overwrites the old entry if the key - /// was already present. Returns the old value in an option if it existed and - /// `null` otherwise, as well as the new map. Compares keys using the provided - /// function `equal`. - /// - /// Example: - /// ```motoko include=import,initialize - /// // Add three entries to the map - /// // map = [(0, 10), (1, 11), (2, 12)] - /// map := AssocList.replace(map, 0, Nat.equal, ?10).0; - /// map := AssocList.replace(map, 1, Nat.equal, ?11).0; - /// map := AssocList.replace(map, 2, Nat.equal, ?12).0; - /// // Override second entry - /// map := AssocList.replace(map, 1, Nat.equal, ?21).0; - /// - /// List.toArray(map) - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func replace( - map : AssocList, - key : K, - equal : (K, K) -> Bool, - value : ?V - ) : (AssocList, ?V) { - var prev : ?V = null; - func del(al : AssocList) : AssocList { - switch (al) { - case (?(kv, tl)) { - if (equal(key, kv.0)) { - prev := ?kv.1; - tl - } else { - let tl1 = del(tl); - switch (prev) { - case null { al }; - case (?_) { ?(kv, tl1) } - } - } - }; - case null { - null - } - } - }; - let map1 = del(map); - switch value { - case (?value) { - (?((key, value), map1), prev) - }; - case null { - (map1, prev) - }; - }; - }; - - /// Produces a new map containing all entries from `map1` whose keys are not - /// contained in `map2`. The "extra" entries in `map2` are ignored. Compares - /// keys using the provided function `equal`. - /// - /// Example: - /// ```motoko include=import,initialize - /// // Create map1 = [(0, 10), (1, 11), (2, 12)] - /// var map1 : AssocList = null; - /// map1 := AssocList.replace(map1, 0, Nat.equal, ?10).0; - /// map1 := AssocList.replace(map1, 1, Nat.equal, ?11).0; - /// map1 := AssocList.replace(map1, 2, Nat.equal, ?12).0; - /// - /// // Create map2 = [(2, 12), (3, 13)] - /// var map2 : AssocList = null; - /// map2 := AssocList.replace(map2, 2, Nat.equal, ?12).0; - /// map2 := AssocList.replace(map2, 3, Nat.equal, ?13).0; - /// - /// // Take the difference - /// let newMap = AssocList.diff(map1, map2, Nat.equal); - /// List.toArray(newMap) - /// ``` - /// Runtime: O(size1 * size2) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func diff( - map1 : AssocList, - map2 : AssocList, - equal : (K, K) -> Bool - ) : AssocList { - func rec(al1 : AssocList) : AssocList { - switch al1 { - case (null) { null }; - case (?((k, v1), tl)) { - switch (find(map2, k, equal)) { - case (null) { ?((k, v1), rec(tl)) }; - case (?v2) { rec(tl) } - } - } - } - }; - rec(map1) - }; - - /// @deprecated - public func mapAppend( - map1 : AssocList, - map2 : AssocList, - f : (?V, ?W) -> X - ) : AssocList { - func rec(al1 : AssocList, al2 : AssocList) : AssocList { - switch (al1, al2) { - case (null, null) { null }; - case (?((k, v), al1_), _) { ?((k, f(?v, null)), rec(al1_, al2)) }; - case (null, ?((k, v), al2_)) { ?((k, f(null, ?v)), rec(null, al2_)) } - } - }; - rec(map1, map2) - }; - - /// Produces a new map by mapping entries in `map1` and `map2` using `f` and - /// concatenating the results. Assumes that there are no collisions between - /// keys in `map1` and `map2`. - /// - /// Example: - /// ```motoko include=import,initialize - /// import { trap } "mo:base/Debug"; - /// - /// // Create map1 = [(0, 10), (1, 11), (2, 12)] - /// var map1 : AssocList = null; - /// map1 := AssocList.replace(map1, 0, Nat.equal, ?10).0; - /// map1 := AssocList.replace(map1, 1, Nat.equal, ?11).0; - /// map1 := AssocList.replace(map1, 2, Nat.equal, ?12).0; - /// - /// // Create map2 = [(4, "14"), (3, "13")] - /// var map2 : AssocList = null; - /// map2 := AssocList.replace(map2, 4, Nat.equal, ?"14").0; - /// map2 := AssocList.replace(map2, 3, Nat.equal, ?"13").0; - /// - /// // Map and append the two AssocLists - /// let newMap = - /// AssocList.disjDisjoint( - /// map1, - /// map2, - /// func((v1, v2) : (?Nat, ?Text)) { - /// switch(v1, v2) { - /// case(?v1, null) { - /// debug_show(v1) // convert values from map1 to Text - /// }; - /// case(null, ?v2) { - /// v2 // keep values from map2 as Text - /// }; - /// case _ { - /// trap "These cases will never happen in mapAppend" - /// } - /// } - /// } - /// ); - /// - /// List.toArray(newMap) - /// ``` - /// Runtime: O(size1 + size2) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func disjDisjoint( - map1 : AssocList, - map2 : AssocList, - f : (?V, ?W) -> X - ) : AssocList { - mapAppend(map1, map2, f) - }; - - /// Creates a new map by merging entries from `map1` and `map2`, and mapping - /// them using `combine`. `combine` is also used to combine the values of colliding keys. - /// Keys are compared using the given `equal` function. - /// - /// NOTE: `combine` will never be applied to `(null, null)`. - /// - /// Example: - /// ```motoko include=import,initialize - /// import { trap } "mo:base/Debug"; - /// - /// // Create map1 = [(0, 10), (1, 11), (2, 12)] - /// var map1 : AssocList = null; - /// map1 := AssocList.replace(map1, 0, Nat.equal, ?10).0; - /// map1 := AssocList.replace(map1, 1, Nat.equal, ?11).0; - /// map1 := AssocList.replace(map1, 2, Nat.equal, ?12).0; - /// - /// // Create map2 = [(2, 12), (3, 13)] - /// var map2 : AssocList = null; - /// map2 := AssocList.replace(map2, 2, Nat.equal, ?12).0; - /// map2 := AssocList.replace(map2, 3, Nat.equal, ?13).0; - /// - /// // Merge the two maps using `combine` - /// let newMap = - /// AssocList.disj( - /// map1, - /// map2, - /// Nat.equal, - /// func((v1, v2) : (?Nat, ?Nat)) : Nat { - /// switch(v1, v2) { - /// case(?v1, ?v2) { - /// v1 + v2 // combine values of colliding keys by adding them - /// }; - /// case(?v1, null) { - /// v1 // when a key doesn't collide, keep the original value - /// }; - /// case(null, ?v2) { - /// v2 - /// }; - /// case _ { - /// trap "This case will never happen in disj" - /// } - /// } - /// } - /// ); - /// - /// List.toArray(newMap) - /// ``` - /// Runtime: O(size1 * size2) - /// - /// Space: O(size1 + size2) - /// - /// *Runtime and space assumes that `equal` and `combine` runs in O(1) time and space. - public func disj( - map1 : AssocList, - map2 : AssocList, - equal : (K, K) -> Bool, - combine : (?V, ?W) -> X - ) : AssocList { - func rec1(al1Rec : AssocList) : AssocList { - switch al1Rec { - case (null) { - func rec2(al2 : AssocList) : AssocList { - switch al2 { - case (null) { null }; - case (?((k, v2), tl)) { - switch (find(map1, k, equal)) { - case (null) { ?((k, combine(null, ?v2)), rec2(tl)) }; - case (?v1) { ?((k, combine(?v1, ?v2)), rec2(tl)) } - } - } - } - }; - rec2(map2) - }; - case (?((k, v1), tl)) { - switch (find(map2, k, equal)) { - case (null) { ?((k, combine(?v1, null)), rec1(tl)) }; - case (?v2) { /* handled above */ rec1(tl) } - } - } - } - }; - rec1(map1) - }; - - /// Takes the intersection of `map1` and `map2`, only keeping colliding keys - /// and combining values using the `combine` function. Keys are compared using - /// the `equal` function. - /// - /// Example: - /// ```motoko include=import,initialize - /// // Create map1 = [(0, 10), (1, 11), (2, 12)] - /// var map1 : AssocList = null; - /// map1 := AssocList.replace(map1, 0, Nat.equal, ?10).0; - /// map1 := AssocList.replace(map1, 1, Nat.equal, ?11).0; - /// map1 := AssocList.replace(map1, 2, Nat.equal, ?12).0; - /// - /// // Create map2 = [(2, 12), (3, 13)] - /// var map2 : AssocList = null; - /// map2 := AssocList.replace(map2, 2, Nat.equal, ?12).0; - /// map2 := AssocList.replace(map2, 3, Nat.equal, ?13).0; - /// - /// // Take the intersection of the two maps, combining values by adding them - /// let newMap = AssocList.join(map1, map2, Nat.equal, Nat.add); - /// - /// List.toArray(newMap) - /// ``` - /// Runtime: O(size1 * size2) - /// - /// Space: O(size1 + size2) - /// - /// *Runtime and space assumes that `equal` and `combine` runs in O(1) time and space. - public func join( - map1 : AssocList, - map2 : AssocList, - equal : (K, K) -> Bool, - combine : (V, W) -> X - ) : AssocList { - func rec(al1 : AssocList) : AssocList { - switch al1 { - case (null) { null }; - case (?((k, v1), tl)) { - switch (find(map2, k, equal)) { - case (null) { rec(tl) }; - case (?v2) { ?((k, combine(v1, v2)), rec(tl)) } - } - } - } - }; - rec(map1) - }; - - /// Collapses the elements in `map` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// Example: - /// ```motoko include=import,initialize - /// // Create map = [(0, 10), (1, 11), (2, 12)] - /// var map : AssocList = null; - /// map := AssocList.replace(map, 0, Nat.equal, ?10).0; - /// map := AssocList.replace(map, 1, Nat.equal, ?11).0; - /// map := AssocList.replace(map, 2, Nat.equal, ?12).0; - /// - /// // (0 * 10) + (1 * 11) + (2 * 12) - /// AssocList.fold(map, 0, func(k, v, sumSoFar) = (k * v) + sumSoFar) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func fold( - map : AssocList, - base : X, - combine : (K, V, X) -> X - ) : X { - func rec(al : AssocList) : X { - switch al { - case null { base }; - case (?((k, v), t)) { combine(k, v, rec(t)) } - } - }; - rec(map) - } -} diff --git a/.mops/base@0.11.1/src/Blob.mo b/.mops/base@0.11.1/src/Blob.mo deleted file mode 100644 index 3b6ccd1..0000000 --- a/.mops/base@0.11.1/src/Blob.mo +++ /dev/null @@ -1,207 +0,0 @@ -/// Module for working with Blobs: immutable sequence of bytes. -/// -/// Blobs represent sequences of bytes. They are immutable, iterable, but not indexable and can be empty. -/// -/// Byte sequences are also often represented as `[Nat8]`, i.e. an array of bytes, but this representation is currently much less compact than `Blob`, taking 4 physical bytes to represent each logical byte in the sequence. -/// If you would like to manipulate Blobs, it is recommended that you convert -/// Blobs to `[var Nat8]` or `Buffer`, do the manipulation, then convert back. -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Blob "mo:base/Blob"; -/// ``` -/// -/// Some built in features not listed in this module: -/// -/// * You can create a `Blob` literal from a `Text` literal, provided the context expects an expression of type `Blob`. -/// * `b.size() : Nat` returns the number of bytes in the blob `b`; -/// * `b.vals() : Iter.Iter` returns an iterator to enumerate the bytes of the blob `b`. -/// -/// For example: -/// ```motoko include=import -/// import Debug "mo:base/Debug"; -/// import Nat8 "mo:base/Nat8"; -/// -/// let blob = "\00\00\00\ff" : Blob; // blob literals, where each byte is delimited by a back-slash and represented in hex -/// let blob2 = "charsもあり" : Blob; // you can also use characters in the literals -/// let numBytes = blob.size(); // => 4 (returns the number of bytes in the Blob) -/// for (byte : Nat8 in blob.vals()) { // iterator over the Blob -/// Debug.print(Nat8.toText(byte)) -/// } -/// ``` -import Prim "mo:⛔"; -module { - public type Blob = Prim.Types.Blob; - /// Creates a `Blob` from an array of bytes (`[Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// let bytes : [Nat8] = [0, 255, 0]; - /// let blob = Blob.fromArray(bytes); // => "\00\FF\00" - /// ``` - public func fromArray(bytes : [Nat8]) : Blob = Prim.arrayToBlob bytes; - - /// Creates a `Blob` from a mutable array of bytes (`[var Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// let bytes : [var Nat8] = [var 0, 255, 0]; - /// let blob = Blob.fromArrayMut(bytes); // => "\00\FF\00" - /// ``` - public func fromArrayMut(bytes : [var Nat8]) : Blob = Prim.arrayMutToBlob bytes; - - /// Converts a `Blob` to an array of bytes (`[Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\00\FF\00" : Blob; - /// let bytes = Blob.toArray(blob); // => [0, 255, 0] - /// ``` - public func toArray(blob : Blob) : [Nat8] = Prim.blobToArray blob; - - /// Converts a `Blob` to a mutable array of bytes (`[var Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\00\FF\00" : Blob; - /// let bytes = Blob.toArrayMut(blob); // => [var 0, 255, 0] - /// ``` - public func toArrayMut(blob : Blob) : [var Nat8] = Prim.blobToArrayMut blob; - - /// Returns the (non-cryptographic) hash of `blob`. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\00\FF\00" : Blob; - /// Blob.hash(blob) // => 1_818_567_776 - /// ``` - public func hash(blob : Blob) : Nat32 = Prim.hashBlob blob; - - /// General purpose comparison function for `Blob` by comparing the value of - /// the bytes. Returns the `Order` (either `#less`, `#equal`, or `#greater`) - /// by comparing `blob1` with `blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\00\00" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// Blob.compare(blob1, blob2) // => #less - /// ``` - public func compare(b1 : Blob, b2 : Blob) : { #less; #equal; #greater } { - let c = Prim.blobCompare(b1, b2); - if (c < 0) #less else if (c == 0) #equal else #greater - }; - - /// Equality function for `Blob` types. - /// This is equivalent to `blob1 == blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\FF\00" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// ignore Blob.equal(blob1, blob2); - /// blob1 == blob2 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function value - /// to pass to a higher order function. It is not possible to use `==` as a - /// function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Buffer "mo:base/Buffer"; - /// - /// let buffer1 = Buffer.Buffer(3); - /// let buffer2 = Buffer.Buffer(3); - /// Buffer.equal(buffer1, buffer2, Blob.equal) // => true - /// ``` - public func equal(blob1 : Blob, blob2 : Blob) : Bool { blob1 == blob2 }; - - /// Inequality function for `Blob` types. - /// This is equivalent to `blob1 != blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\AA\AA" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// ignore Blob.notEqual(blob1, blob2); - /// blob1 != blob2 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function value - /// to pass to a higher order function. It is not possible to use `!=` as a - /// function value at the moment. - public func notEqual(blob1 : Blob, blob2 : Blob) : Bool { blob1 != blob2 }; - - /// "Less than" function for `Blob` types. - /// This is equivalent to `blob1 < blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\AA\AA" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// ignore Blob.less(blob1, blob2); - /// blob1 < blob2 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function value - /// to pass to a higher order function. It is not possible to use `<` as a - /// function value at the moment. - public func less(blob1 : Blob, blob2 : Blob) : Bool { blob1 < blob2 }; - - /// "Less than or equal to" function for `Blob` types. - /// This is equivalent to `blob1 <= blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\AA\AA" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// ignore Blob.lessOrEqual(blob1, blob2); - /// blob1 <= blob2 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function value - /// to pass to a higher order function. It is not possible to use `<=` as a - /// function value at the moment. - public func lessOrEqual(blob1 : Blob, blob2 : Blob) : Bool { blob1 <= blob2 }; - - /// "Greater than" function for `Blob` types. - /// This is equivalent to `blob1 > blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\BB\AA\AA" : Blob; - /// let blob2 = "\00\00\00" : Blob; - /// ignore Blob.greater(blob1, blob2); - /// blob1 > blob2 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function value - /// to pass to a higher order function. It is not possible to use `>` as a - /// function value at the moment. - public func greater(blob1 : Blob, blob2 : Blob) : Bool { blob1 > blob2 }; - - /// "Greater than or equal to" function for `Blob` types. - /// This is equivalent to `blob1 >= blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\BB\AA\AA" : Blob; - /// let blob2 = "\00\00\00" : Blob; - /// ignore Blob.greaterOrEqual(blob1, blob2); - /// blob1 >= blob2 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function value - /// to pass to a higher order function. It is not possible to use `>=` as a - /// function value at the moment. - public func greaterOrEqual(blob1 : Blob, blob2 : Blob) : Bool { - blob1 >= blob2 - } -} diff --git a/.mops/base@0.11.1/src/Bool.mo b/.mops/base@0.11.1/src/Bool.mo deleted file mode 100644 index bb74545..0000000 --- a/.mops/base@0.11.1/src/Bool.mo +++ /dev/null @@ -1,44 +0,0 @@ -/// Boolean type and operations. -/// -/// While boolean operators `_ and _` and `_ or _` are short-circuiting, -/// avoiding computation of the right argument when possible, the functions -/// `logand(_, _)` and `logor(_, _)` are *strict* and will always evaluate *both* -/// of their arguments. - -import Prim "mo:⛔"; -module { - - /// Booleans with constants `true` and `false`. - public type Bool = Prim.Types.Bool; - - /// Conversion. - public func toText(x : Bool) : Text { - if x { "true" } else { "false" } - }; - - /// Returns `x and y`. - public func logand(x : Bool, y : Bool) : Bool { x and y }; - - /// Returns `x or y`. - public func logor(x : Bool, y : Bool) : Bool { x or y }; - - /// Returns exclusive or of `x` and `y`, `x != y`. - public func logxor(x : Bool, y : Bool) : Bool { - x != y - }; - - /// Returns `not x`. - public func lognot(x : Bool) : Bool { not x }; - - /// Returns `x == y`. - public func equal(x : Bool, y : Bool) : Bool { x == y }; - - /// Returns `x != y`. - public func notEqual(x : Bool, y : Bool) : Bool { x != y }; - - /// Returns the order of `x` and `y`, where `false < true`. - public func compare(x : Bool, y : Bool) : { #less; #equal; #greater } { - if (x == y) { #equal } else if (x) { #greater } else { #less } - }; - -} diff --git a/.mops/base@0.11.1/src/Buffer.mo b/.mops/base@0.11.1/src/Buffer.mo deleted file mode 100644 index 219ac78..0000000 --- a/.mops/base@0.11.1/src/Buffer.mo +++ /dev/null @@ -1,2660 +0,0 @@ -/// Class `Buffer` provides a mutable list of elements of type `X`. -/// The class wraps and resizes an underyling array that holds the elements, -/// and thus is comparable to ArrayLists or Vectors in other languages. -/// -/// When required, the current state of a buffer object can be converted to a fixed-size array of its elements. -/// This is recommended for example when storing a buffer to a stable variable. -/// -/// Throughout this documentation, two terms come up that can be confused: `size` -/// and `capacity`. `size` is the length of the list that the buffer represents. -/// `capacity` is the length of the underyling array that backs this list. -/// `capacity` >= `size` is an invariant for this class. -/// -/// Like arrays, elements in the buffer are ordered by indices from 0 to `size`-1. -/// -/// WARNING: Certain operations are amortized O(1) time, such as `add`, but run -/// in worst case O(n) time. These worst case runtimes may exceed the cycles limit -/// per message if the size of the buffer is large enough. Grow these structures -/// with discretion. All amortized operations below also list the worst case runtime. -/// -/// Constructor: -/// The argument `initCapacity` determines the initial capacity of the array. -/// The underlying array grows by a factor of 1.5 when its current capacity is -/// exceeded. Further, when the size of the buffer shrinks to be less than 1/4th -/// of the capacity, the underyling array is shrunk by a factor of 2. -/// -/// Example: -/// ```motoko name=initialize -/// import Buffer "mo:base/Buffer"; -/// -/// let buffer = Buffer.Buffer(3); // Creates a new Buffer -/// ``` -/// -/// Runtime: O(initCapacity) -/// -/// Space: O(initCapacity) - -import Prim "mo:⛔"; -import Result "Result"; -import Order "Order"; -import Array "Array"; - -module { - type Order = Order.Order; - - // The following constants are used to manage the capacity. - // The length of `elements` is increased by `INCREASE_FACTOR` when capacity is reached. - // The length of `elements` is decreased by `DECREASE_FACTOR` when capacity is strictly less than - // `DECREASE_THRESHOLD`. - - // INCREASE_FACTOR = INCREASE_FACTOR_NUME / INCREASE_FACTOR_DENOM (with floating point division) - // Keep INCREASE_FACTOR low to minimize cycle limit problem - private let INCREASE_FACTOR_NUME = 3; - private let INCREASE_FACTOR_DENOM = 2; - private let DECREASE_THRESHOLD = 4; // Don't decrease capacity too early to avoid thrashing - private let DECREASE_FACTOR = 2; - private let DEFAULT_CAPACITY = 8; - - private func newCapacity(oldCapacity : Nat) : Nat { - if (oldCapacity == 0) { - 1 - } else { - // calculates ceil(oldCapacity * INCREASE_FACTOR) without floats - ((oldCapacity * INCREASE_FACTOR_NUME) + INCREASE_FACTOR_DENOM - 1) / INCREASE_FACTOR_DENOM - } - }; - - public class Buffer(initCapacity : Nat) = this { - var _size : Nat = 0; // avoid name clash with `size()` method - var elements : [var ?X] = Prim.Array_init(initCapacity, null); - - /// Returns the current number of elements in the buffer. - /// - /// Example: - /// ```motoko include=initialize - /// buffer.size() // => 0 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func size() : Nat = _size; - - /// Adds a single element to the end of the buffer, doubling - /// the size of the array if capacity is exceeded. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(0); // add 0 to buffer - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); // causes underlying array to increase in capacity - /// Buffer.toArray(buffer) // => [0, 1, 2, 3] - /// ``` - /// - /// Amortized Runtime: O(1), Worst Case Runtime: O(size) - /// - /// Amortized Space: O(1), Worst Case Space: O(size) - public func add(element : X) { - if (_size == elements.size()) { - reserve(newCapacity(elements.size())) - }; - elements[_size] := ?element; - _size += 1 - }; - - /// Returns the element at index `index`. Traps if `index >= size`. Indexing is zero-based. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(10); - /// buffer.add(11); - /// buffer.get(0); // => 10 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func get(index : Nat) : X { - switch (elements[index]) { - case (?element) element; - case null Prim.trap("Buffer index out of bounds in get") - } - }; - - /// Returns the element at index `index` as an option. - /// Returns `null` when `index >= size`. Indexing is zero-based. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(10); - /// buffer.add(11); - /// let x = buffer.getOpt(0); // => ?10 - /// let y = buffer.getOpt(2); // => null - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func getOpt(index : Nat) : ?X { - if (index < _size) { - elements[index] - } else { - null - } - }; - - /// Overwrites the current element at `index` with `element`. Traps if - /// `index` >= size. Indexing is zero-based. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(10); - /// buffer.put(0, 20); // overwrites 10 at index 0 with 20 - /// Buffer.toArray(buffer) // => [20] - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func put(index : Nat, element : X) { - if (index >= _size) { - Prim.trap "Buffer index out of bounds in put" - }; - elements[index] := ?element - }; - - /// Removes and returns the last item in the buffer or `null` if - /// the buffer is empty. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(10); - /// buffer.add(11); - /// buffer.removeLast(); // => ?11 - /// ``` - /// - /// Amortized Runtime: O(1), Worst Case Runtime: O(size) - /// - /// Amortized Space: O(1), Worst Case Space: O(size) - public func removeLast() : ?X { - if (_size == 0) { - return null - }; - - _size -= 1; - let lastElement = elements[_size]; - elements[_size] := null; - - if (_size < elements.size() / DECREASE_THRESHOLD) { - // FIXME should this new capacity be a function of _size - // instead of the current capacity? E.g. _size * INCREASE_FACTOR - reserve(elements.size() / DECREASE_FACTOR) - }; - - lastElement - }; - - /// Removes and returns the element at `index` from the buffer. - /// All elements with index > `index` are shifted one position to the left. - /// This may cause a downsizing of the array. - /// - /// Traps if index >= size. - /// - /// WARNING: Repeated removal of elements using this method is ineffecient - /// and might be a sign that you should consider a different data-structure - /// for your use case. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(10); - /// buffer.add(11); - /// buffer.add(12); - /// let x = buffer.remove(1); // evaluates to 11. 11 no longer in list. - /// Buffer.toArray(buffer) // => [10, 12] - /// ``` - /// - /// Runtime: O(size) - /// - /// Amortized Space: O(1), Worst Case Space: O(size) - public func remove(index : Nat) : X { - if (index >= _size) { - Prim.trap "Buffer index out of bounds in remove" - }; - - let element = elements[index]; - - // copy elements to new array and shift over in one pass - if ((_size - 1) : Nat < elements.size() / DECREASE_THRESHOLD) { - let elements2 = Prim.Array_init(elements.size() / DECREASE_FACTOR, null); - - var i = 0; - var j = 0; - label l while (i < _size) { - if (i == index) { - i += 1; - continue l - }; - - elements2[j] := elements[i]; - i += 1; - j += 1 - }; - elements := elements2 - } else { - // just shift over elements - var i = index; - while (i < (_size - 1 : Nat)) { - elements[i] := elements[i + 1]; - i += 1 - }; - elements[_size - 1] := null - }; - - _size -= 1; - - switch (element) { - case (?element) { - element - }; - case null { - Prim.trap "Malformed buffer in remove" - } - } - }; - - /// Resets the buffer. Capacity is set to 8. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(10); - /// buffer.add(11); - /// buffer.add(12); - /// buffer.clear(); // buffer is now empty - /// Buffer.toArray(buffer) // => [] - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func clear() { - _size := 0; - reserve(DEFAULT_CAPACITY) - }; - - /// Removes all elements from the buffer for which the predicate returns false. - /// The predicate is given both the index of the element and the element itself. - /// This may cause a downsizing of the array. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(10); - /// buffer.add(11); - /// buffer.add(12); - /// buffer.filterEntries(func(_, x) = x % 2 == 0); // only keep even elements - /// Buffer.toArray(buffer) // => [10, 12] - /// ``` - /// - /// Runtime: O(size) - /// - /// Amortized Space: O(1), Worst Case Space: O(size) - public func filterEntries(predicate : (Nat, X) -> Bool) { - var numRemoved = 0; - let keep = Prim.Array_tabulate( - _size, - func i { - switch (elements[i]) { - case (?element) { - if (predicate(i, element)) { - true - } else { - numRemoved += 1; - false - } - }; - case null { - Prim.trap "Malformed buffer in filter()" - } - } - } - ); - - let capacity = elements.size(); - - if ((_size - numRemoved : Nat) < capacity / DECREASE_THRESHOLD) { - let elements2 = Prim.Array_init(capacity / DECREASE_FACTOR, null); - - var i = 0; - var j = 0; - while (i < _size) { - if (keep[i]) { - elements2[j] := elements[i]; - i += 1; - j += 1 - } else { - i += 1 - } - }; - - elements := elements2 - } else { - var i = 0; - var j = 0; - while (i < _size) { - if (keep[i]) { - elements[j] := elements[i]; - i += 1; - j += 1 - } else { - i += 1 - } - }; - - while (j < _size) { - elements[j] := null; - j += 1 - } - }; - - _size -= numRemoved - }; - - /// Returns the capacity of the buffer (the length of the underlying array). - /// - /// Example: - /// ```motoko include=initialize - /// - /// let buffer = Buffer.Buffer(2); // underlying array has capacity 2 - /// buffer.add(10); - /// let c1 = buffer.capacity(); // => 2 - /// buffer.add(11); - /// buffer.add(12); // causes capacity to increase by factor of 1.5 - /// let c2 = buffer.capacity(); // => 3 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func capacity() : Nat = elements.size(); - - /// Changes the capacity to `capacity`. Traps if `capacity` < `size`. - /// - /// ```motoko include=initialize - /// - /// buffer.reserve(4); - /// buffer.add(10); - /// buffer.add(11); - /// buffer.capacity(); // => 4 - /// ``` - /// - /// Runtime: O(capacity) - /// - /// Space: O(capacity) - public func reserve(capacity : Nat) { - if (capacity < _size) { - Prim.trap "capacity must be >= size in reserve" - }; - - let elements2 = Prim.Array_init(capacity, null); - - var i = 0; - while (i < _size) { - elements2[i] := elements[i]; - i += 1 - }; - elements := elements2 - }; - - /// Adds all elements in buffer `b` to this buffer. - /// - /// ```motoko include=initialize - /// let buffer1 = Buffer.Buffer(2); - /// let buffer2 = Buffer.Buffer(2); - /// buffer1.add(10); - /// buffer1.add(11); - /// buffer2.add(12); - /// buffer2.add(13); - /// buffer1.append(buffer2); // adds elements from buffer2 to buffer1 - /// Buffer.toArray(buffer1) // => [10, 11, 12, 13] - /// ``` - /// - /// Amortized Runtime: O(size2), Worst Case Runtime: O(size1 + size2) - /// - /// Amortized Space: O(1), Worst Case Space: O(size1 + size2) - public func append(buffer2 : Buffer) { - let size2 = buffer2.size(); - // Make sure you only allocate a new array at most once - if (_size + size2 > elements.size()) { - // FIXME would be nice to have a tabulate for var arrays here - reserve(newCapacity(_size + size2)) - }; - var i = 0; - while (i < size2) { - elements[_size + i] := buffer2.getOpt i; - i += 1 - }; - - _size += size2 - }; - - /// Inserts `element` at `index`, shifts all elements to the right of - /// `index` over by one index. Traps if `index` is greater than size. - /// - /// ```motoko include=initialize - /// let buffer1 = Buffer.Buffer(2); - /// let buffer2 = Buffer.Buffer(2); - /// buffer.add(10); - /// buffer.add(11); - /// buffer.insert(1, 9); - /// Buffer.toArray(buffer) // => [10, 9, 11] - /// ``` - /// - /// Runtime: O(size) - /// - /// Amortized Space: O(1), Worst Case Space: O(size) - public func insert(index : Nat, element : X) { - if (index > _size) { - Prim.trap "Buffer index out of bounds in insert" - }; - let capacity = elements.size(); - - if (_size + 1 > capacity) { - let capacity = elements.size(); - let elements2 = Prim.Array_init(newCapacity capacity, null); - var i = 0; - while (i < _size + 1) { - if (i < index) { - elements2[i] := elements[i] - } else if (i == index) { - elements2[i] := ?element - } else { - elements2[i] := elements[i - 1] - }; - - i += 1 - }; - elements := elements2 - } else { - var i : Nat = _size; - while (i > index) { - elements[i] := elements[i - 1]; - i -= 1 - }; - elements[index] := ?element - }; - - _size += 1 - }; - - /// Inserts `buffer2` at `index`, and shifts all elements to the right of - /// `index` over by size2. Traps if `index` is greater than size. - /// - /// ```motoko include=initialize - /// let buffer1 = Buffer.Buffer(2); - /// let buffer2 = Buffer.Buffer(2); - /// buffer1.add(10); - /// buffer1.add(11); - /// buffer2.add(12); - /// buffer2.add(13); - /// buffer1.insertBuffer(1, buffer2); - /// Buffer.toArray(buffer1) // => [10, 12, 13, 11] - /// ``` - /// - /// Runtime: O(size) - /// - /// Amortized Space: O(1), Worst Case Space: O(size1 + size2) - public func insertBuffer(index : Nat, buffer2 : Buffer) { - if (index > _size) { - Prim.trap "Buffer index out of bounds in insertBuffer" - }; - - let size2 = buffer2.size(); - let capacity = elements.size(); - - // copy elements to new array and shift over in one pass - if (_size + size2 > capacity) { - let elements2 = Prim.Array_init(newCapacity(_size + size2), null); - var i = 0; - for (element in elements.vals()) { - if (i == index) { - i += size2 - }; - elements2[i] := element; - i += 1 - }; - - i := 0; - while (i < size2) { - elements2[i + index] := buffer2.getOpt(i); - i += 1 - }; - elements := elements2 - } // just insert - else { - var i = index; - while (i < index + size2) { - if (i < _size) { - elements[i + size2] := elements[i] - }; - elements[i] := buffer2.getOpt(i - index); - - i += 1 - } - }; - - _size += size2 - }; - - /// Sorts the elements in the buffer according to `compare`. - /// Sort is deterministic, stable, and in-place. - /// - /// ```motoko include=initialize - /// - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(11); - /// buffer.add(12); - /// buffer.add(10); - /// buffer.sort(Nat.compare); - /// Buffer.toArray(buffer) // => [10, 11, 12] - /// ``` - /// - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - public func sort(compare : (X, X) -> Order.Order) { - // Stable merge sort in a bottom-up iterative style - if (_size == 0) { - return - }; - let scratchSpace = Prim.Array_init(_size, null); - - let sizeDec = _size - 1 : Nat; - var currSize = 1; // current size of the subarrays being merged - // when the current size == size, the array has been merged into a single sorted array - while (currSize < _size) { - var leftStart = 0; // selects the current left subarray being merged - while (leftStart < sizeDec) { - let mid : Nat = if (leftStart + currSize - 1 : Nat < sizeDec) { - leftStart + currSize - 1 - } else { sizeDec }; - let rightEnd : Nat = if (leftStart + (2 * currSize) - 1 : Nat < sizeDec) { - leftStart + (2 * currSize) - 1 - } else { sizeDec }; - - // Merge subarrays elements[leftStart...mid] and elements[mid+1...rightEnd] - var left = leftStart; - var right = mid + 1; - var nextSorted = leftStart; - while (left < mid + 1 and right < rightEnd + 1) { - let leftOpt = elements[left]; - let rightOpt = elements[right]; - switch (leftOpt, rightOpt) { - case (?leftElement, ?rightElement) { - switch (compare(leftElement, rightElement)) { - case (#less or #equal) { - scratchSpace[nextSorted] := leftOpt; - left += 1 - }; - case (#greater) { - scratchSpace[nextSorted] := rightOpt; - right += 1 - } - } - }; - case (_, _) { - // only sorting non-null items - Prim.trap "Malformed buffer in sort" - } - }; - nextSorted += 1 - }; - while (left < mid + 1) { - scratchSpace[nextSorted] := elements[left]; - nextSorted += 1; - left += 1 - }; - while (right < rightEnd + 1) { - scratchSpace[nextSorted] := elements[right]; - nextSorted += 1; - right += 1 - }; - - // Copy over merged elements - var i = leftStart; - while (i < rightEnd + 1) { - elements[i] := scratchSpace[i]; - i += 1 - }; - - leftStart += 2 * currSize - }; - currSize *= 2 - } - }; - - /// Returns an Iterator (`Iter`) over the elements of this buffer. - /// Iterator provides a single method `next()`, which returns - /// elements in order, or `null` when out of elements to iterate over. - /// - /// ```motoko include=initialize - /// - /// buffer.add(10); - /// buffer.add(11); - /// buffer.add(12); - /// - /// var sum = 0; - /// for (element in buffer.vals()) { - /// sum += element; - /// }; - /// sum // => 33 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func vals() : { next : () -> ?X } = object { - // FIXME either handle modification to underlying list - // or explicitly warn users in documentation - var nextIndex = 0; - public func next() : ?X { - if (nextIndex >= _size) { - return null - }; - let nextElement = elements[nextIndex]; - nextIndex += 1; - nextElement - } - }; - - // FOLLOWING METHODS ARE DEPRECATED - - /// @deprecated Use static library function instead. - public func clone() : Buffer { - let newBuffer = Buffer(elements.size()); - for (element in vals()) { - newBuffer.add(element) - }; - newBuffer - }; - - /// @deprecated Use static library function instead. - public func toArray() : [X] = - // immutable clone of array - Prim.Array_tabulate( - _size, - func(i : Nat) : X { get i } - ); - - /// @deprecated Use static library function instead. - public func toVarArray() : [var X] { - if (_size == 0) { [var] } else { - let newArray = Prim.Array_init(_size, get 0); - var i = 0; - for (element in vals()) { - newArray[i] := element; - i += 1 - }; - newArray - } - } - }; - - /// Returns true if and only if the buffer is empty. - /// - /// Example: - /// ```motoko include=initialize - /// buffer.add(2); - /// buffer.add(0); - /// buffer.add(3); - /// Buffer.isEmpty(buffer); // => false - /// ``` - /// - /// ```motoko include=initialize - /// Buffer.isEmpty(buffer); // => true - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func isEmpty(buffer : Buffer) : Bool = buffer.size() == 0; - - /// Returns true iff `buffer` contains `element` with respect to equality - /// defined by `equal`. - /// - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(2); - /// buffer.add(0); - /// buffer.add(3); - /// Buffer.contains(buffer, 2, Nat.equal); // => true - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func contains(buffer : Buffer, element : X, equal : (X, X) -> Bool) : Bool { - for (current in buffer.vals()) { - if (equal(current, element)) { - return true - } - }; - - false - }; - - /// Returns a copy of `buffer`, with the same capacity. - /// - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(1); - /// - /// let clone = Buffer.clone(buffer); - /// Buffer.toArray(clone); // => [1] - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func clone(buffer : Buffer) : Buffer { - let newBuffer = Buffer(buffer.capacity()); - for (element in buffer.vals()) { - newBuffer.add(element) - }; - newBuffer - }; - - /// Finds the greatest element in `buffer` defined by `compare`. - /// Returns `null` if `buffer` is empty. - /// - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// - /// Buffer.max(buffer, Nat.compare); // => ?2 - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func max(buffer : Buffer, compare : (X, X) -> Order) : ?X { - if (buffer.size() == 0) { - return null - }; - - var maxSoFar = buffer.get(0); - for (current in buffer.vals()) { - switch (compare(current, maxSoFar)) { - case (#greater) { - maxSoFar := current - }; - case _ {} - } - }; - - ?maxSoFar - }; - - /// Finds the least element in `buffer` defined by `compare`. - /// Returns `null` if `buffer` is empty. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// - /// Buffer.min(buffer, Nat.compare); // => ?1 - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func min(buffer : Buffer, compare : (X, X) -> Order) : ?X { - if (buffer.size() == 0) { - return null - }; - - var minSoFar = buffer.get(0); - for (current in buffer.vals()) { - switch (compare(current, minSoFar)) { - case (#less) { - minSoFar := current - }; - case _ {} - } - }; - - ?minSoFar - }; - - /// Defines equality for two buffers, using `equal` to recursively compare elements in the - /// buffers. Returns true iff the two buffers are of the same size, and `equal` - /// evaluates to true for every pair of elements in the two buffers of the same - /// index. - /// - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// let buffer1 = Buffer.Buffer(2); - /// buffer1.add(1); - /// buffer1.add(2); - /// - /// let buffer2 = Buffer.Buffer(5); - /// buffer2.add(1); - /// buffer2.add(2); - /// - /// Buffer.equal(buffer1, buffer2, Nat.equal); // => true - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func equal(buffer1 : Buffer, buffer2 : Buffer, equal : (X, X) -> Bool) : Bool { - let size1 = buffer1.size(); - - if (size1 != buffer2.size()) { - return false - }; - - var i = 0; - while (i < size1) { - if (not equal(buffer1.get(i), buffer2.get(i))) { - return false - }; - i += 1 - }; - - true - }; - - /// Defines comparison for two buffers, using `compare` to recursively compare elements in the - /// buffers. Comparison is defined lexicographically. - /// - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// let buffer1 = Buffer.Buffer(2); - /// buffer1.add(1); - /// buffer1.add(2); - /// - /// let buffer2 = Buffer.Buffer(3); - /// buffer2.add(3); - /// buffer2.add(4); - /// - /// Buffer.compare(buffer1, buffer2, Nat.compare); // => #less - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func compare(buffer1 : Buffer, buffer2 : Buffer, compare : (X, X) -> Order.Order) : Order.Order { - let size1 = buffer1.size(); - let size2 = buffer2.size(); - let minSize = if (size1 < size2) { size1 } else { size2 }; - - var i = 0; - while (i < minSize) { - switch (compare(buffer1.get(i), buffer2.get(i))) { - case (#less) { - return #less - }; - case (#greater) { - return #greater - }; - case _ {} - }; - i += 1 - }; - - if (size1 < size2) { - #less - } else if (size1 == size2) { - #equal - } else { - #greater - } - }; - - /// Creates a textual representation of `buffer`, using `toText` to recursively - /// convert the elements into Text. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// - /// Buffer.toText(buffer, Nat.toText); // => "[1, 2, 3, 4]" - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `toText` runs in O(1) time and space. - public func toText(buffer : Buffer, toText : X -> Text) : Text { - let size : Int = buffer.size(); - var i = 0; - var text = ""; - while (i < size - 1) { - text := text # toText(buffer.get(i)) # ", "; // Text implemented as rope - i += 1 - }; - if (size > 0) { - // avoid the trailing comma - text := text # toText(buffer.get(i)) - }; - - "[" # text # "]" - }; - - /// Hashes `buffer` using `hash` to hash the underlying elements. - /// The deterministic hash function is a function of the elements in the Buffer, as well - /// as their ordering. - /// - /// Example: - /// ```motoko include=initialize - /// import Hash "mo:base/Hash"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(1000); - /// - /// Buffer.hash(buffer, Hash.hash); // => 2_872_640_342 - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `hash` runs in O(1) time and space. - public func hash(buffer : Buffer, hash : X -> Nat32) : Nat32 { - let size = buffer.size(); - var i = 0; - var accHash : Nat32 = 0; - - while (i < size) { - accHash := Prim.intToNat32Wrap(i) ^ accHash ^ hash(buffer.get(i)); - i += 1 - }; - - accHash - }; - - /// Finds the first index of `element` in `buffer` using equality of elements defined - /// by `equal`. Returns `null` if `element` is not found. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// - /// Buffer.indexOf(3, buffer, Nat.equal); // => ?2 - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func indexOf(element : X, buffer : Buffer, equal : (X, X) -> Bool) : ?Nat { - let size = buffer.size(); - var i = 0; - while (i < size) { - if (equal(buffer.get(i), element)) { - return ?i - }; - i += 1 - }; - - null - }; - - /// Finds the last index of `element` in `buffer` using equality of elements defined - /// by `equal`. Returns `null` if `element` is not found. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// buffer.add(2); - /// buffer.add(2); - /// - /// Buffer.lastIndexOf(2, buffer, Nat.equal); // => ?5 - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func lastIndexOf(element : X, buffer : Buffer, equal : (X, X) -> Bool) : ?Nat { - let size = buffer.size(); - if (size == 0) { - return null - }; - var i = size; - while (i >= 1) { - i -= 1; - if (equal(buffer.get(i), element)) { - return ?i - } - }; - - null - }; - - /// Searches for `subBuffer` in `buffer`, and returns the starting index if it is found. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// buffer.add(5); - /// buffer.add(6); - /// - /// let sub = Buffer.Buffer(2); - /// sub.add(4); - /// sub.add(5); - /// sub.add(6); - /// - /// Buffer.indexOfBuffer(sub, buffer, Nat.equal); // => ?3 - /// ``` - /// - /// Runtime: O(size of buffer + size of subBuffer) - /// - /// Space: O(size of subBuffer) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func indexOfBuffer(subBuffer : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : ?Nat { - // Uses the KMP substring search algorithm - // Implementation from: https://www.educative.io/answers/what-is-the-knuth-morris-pratt-algorithm - let size = buffer.size(); - let subSize = subBuffer.size(); - if (subSize > size or subSize == 0) { - return null - }; - - // precompute lps - let lps = Prim.Array_init(subSize, 0); - var i = 0; - var j = 1; - - while (j < subSize) { - if (equal(subBuffer.get(i), subBuffer.get(j))) { - i += 1; - lps[j] := i; - j += 1 - } else if (i == 0) { - lps[j] := 0; - j += 1 - } else { - i := lps[i - 1] - } - }; - - // start search - i := 0; - j := 0; - let subSizeDec = subSize - 1 : Nat; // hoisting loop invariant - while (i < subSize and j < size) { - if (equal(subBuffer.get(i), buffer.get(j)) and i == subSizeDec) { - return ?(j - i) - } else if (equal(subBuffer.get(i), buffer.get(j))) { - i += 1; - j += 1 - } else { - if (i != 0) { - i := lps[i - 1] - } else { - j += 1 - } - } - }; - - null - }; - - /// Similar to indexOf, but runs in logarithmic time. Assumes that `buffer` is sorted. - /// Behavior is undefined if `buffer` is not sorted. Uses `compare` to - /// perform the search. Returns an index of `element` if it is found. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(4); - /// buffer.add(5); - /// buffer.add(6); - /// - /// Buffer.binarySearch(5, buffer, Nat.compare); // => ?2 - /// ``` - /// - /// Runtime: O(log(size)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func binarySearch(element : X, buffer : Buffer, compare : (X, X) -> Order.Order) : ?Nat { - var low = 0; - var high = buffer.size(); - - while (low < high) { - let mid = (low + high) / 2; - let current = buffer.get(mid); - switch (compare(element, current)) { - case (#equal) { - return ?mid - }; - case (#less) { - high := mid - }; - case (#greater) { - low := mid + 1 - } - } - }; - - null - }; - - /// Returns the sub-buffer of `buffer` starting at index `start` - /// of length `length`. Traps if `start` is out of bounds, or `start + length` - /// is greater than the size of `buffer`. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// buffer.add(5); - /// buffer.add(6); - /// - /// let sub = Buffer.subBuffer(buffer, 3, 2); - /// Buffer.toText(sub, Nat.toText); // => [4, 5] - /// ``` - /// - /// Runtime: O(length) - /// - /// Space: O(length) - public func subBuffer(buffer : Buffer, start : Nat, length : Nat) : Buffer { - let size = buffer.size(); - let end = start + length; // exclusive - if (start >= size or end > size) { - Prim.trap "Buffer index out of bounds in subBuffer" - }; - - let newBuffer = Buffer(newCapacity length); - - var i = start; - while (i < end) { - newBuffer.add(buffer.get(i)); - - i += 1 - }; - - newBuffer - }; - - /// Checks if `subBuffer` is a sub-Buffer of `buffer`. Uses `equal` to - /// compare elements. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// buffer.add(5); - /// buffer.add(6); - /// - /// let sub = Buffer.Buffer(2); - /// sub.add(2); - /// sub.add(3); - /// Buffer.isSubBufferOf(sub, buffer, Nat.equal); // => true - /// ``` - /// - /// Runtime: O(size of subBuffer + size of buffer) - /// - /// Space: O(size of subBuffer) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func isSubBufferOf(subBuffer : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : Bool { - switch (indexOfBuffer(subBuffer, buffer, equal)) { - case null subBuffer.size() == 0; - case _ true - } - }; - - /// Checks if `subBuffer` is a strict subBuffer of `buffer`, i.e. `subBuffer` must be - /// strictly contained inside both the first and last indices of `buffer`. - /// Uses `equal` to compare elements. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// - /// let sub = Buffer.Buffer(2); - /// sub.add(2); - /// sub.add(3); - /// Buffer.isStrictSubBufferOf(sub, buffer, Nat.equal); // => true - /// ``` - /// - /// Runtime: O(size of subBuffer + size of buffer) - /// - /// Space: O(size of subBuffer) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func isStrictSubBufferOf(subBuffer : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : Bool { - let subBufferSize = subBuffer.size(); - - switch (indexOfBuffer(subBuffer, buffer, equal)) { - case (?index) { - index != 0 and index != (buffer.size() - subBufferSize : Nat) // enforce strictness - }; - case null { - subBufferSize == 0 and subBufferSize != buffer.size() - } - } - }; - - /// Returns the prefix of `buffer` of length `length`. Traps if `length` - /// is greater than the size of `buffer`. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// - /// let pre = Buffer.prefix(buffer, 3); // => [1, 2, 3] - /// Buffer.toText(pre, Nat.toText); - /// ``` - /// - /// Runtime: O(length) - /// - /// Space: O(length) - public func prefix(buffer : Buffer, length : Nat) : Buffer { - let size = buffer.size(); - if (length > size) { - Prim.trap "Buffer index out of bounds in prefix" - }; - - let newBuffer = Buffer(newCapacity length); - - var i = 0; - while (i < length) { - newBuffer.add(buffer.get(i)); - i += 1 - }; - - newBuffer - }; - - /// Checks if `prefix` is a prefix of `buffer`. Uses `equal` to - /// compare elements. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// - /// let pre = Buffer.Buffer(2); - /// pre.add(1); - /// pre.add(2); - /// Buffer.isPrefixOf(pre, buffer, Nat.equal); // => true - /// ``` - /// - /// Runtime: O(size of prefix) - /// - /// Space: O(size of prefix) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func isPrefixOf(prefix : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : Bool { - let sizePrefix = prefix.size(); - if (buffer.size() < sizePrefix) { - return false - }; - - var i = 0; - while (i < sizePrefix) { - if (not equal(buffer.get(i), prefix.get(i))) { - return false - }; - - i += 1 - }; - - return true - }; - - /// Checks if `prefix` is a strict prefix of `buffer`. Uses `equal` to - /// compare elements. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// - /// let pre = Buffer.Buffer(3); - /// pre.add(1); - /// pre.add(2); - /// pre.add(3); - /// Buffer.isStrictPrefixOf(pre, buffer, Nat.equal); // => true - /// ``` - /// - /// Runtime: O(size of prefix) - /// - /// Space: O(size of prefix) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func isStrictPrefixOf(prefix : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : Bool { - if (buffer.size() <= prefix.size()) { - return false - }; - isPrefixOf(prefix, buffer, equal) - }; - - /// Returns the suffix of `buffer` of length `length`. - /// Traps if `length`is greater than the size of `buffer`. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// - /// let suf = Buffer.suffix(buffer, 3); // => [2, 3, 4] - /// Buffer.toText(suf, Nat.toText); - /// ``` - /// - /// Runtime: O(length) - /// - /// Space: O(length) - public func suffix(buffer : Buffer, length : Nat) : Buffer { - let size = buffer.size(); - - if (length > size) { - Prim.trap "Buffer index out of bounds in suffix" - }; - - let newBuffer = Buffer(newCapacity length); - - var i = size - length : Nat; - while (i < size) { - newBuffer.add(buffer.get(i)); - - i += 1 - }; - - newBuffer - }; - - /// Checks if `suffix` is a suffix of `buffer`. Uses `equal` to compare - /// elements. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// - /// let suf = Buffer.Buffer(3); - /// suf.add(2); - /// suf.add(3); - /// suf.add(4); - /// Buffer.isSuffixOf(suf, buffer, Nat.equal); // => true - /// ``` - /// - /// Runtime: O(length of suffix) - /// - /// Space: O(length of suffix) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func isSuffixOf(suffix : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : Bool { - let suffixSize = suffix.size(); - let bufferSize = buffer.size(); - if (bufferSize < suffixSize) { - return false - }; - - var i = bufferSize; - var j = suffixSize; - while (i >= 1 and j >= 1) { - i -= 1; - j -= 1; - if (not equal(buffer.get(i), suffix.get(j))) { - return false - } - }; - - return true - }; - - /// Checks if `suffix` is a strict suffix of `buffer`. Uses `equal` to compare - /// elements. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// - /// let suf = Buffer.Buffer(3); - /// suf.add(2); - /// suf.add(3); - /// suf.add(4); - /// Buffer.isStrictSuffixOf(suf, buffer, Nat.equal); // => true - /// ``` - /// - /// Runtime: O(length of suffix) - /// - /// Space: O(length of suffix) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func isStrictSuffixOf(suffix : Buffer, buffer : Buffer, equal : (X, X) -> Bool) : Bool { - if (buffer.size() <= suffix.size()) { - return false - }; - isSuffixOf(suffix, buffer, equal) - }; - - /// Returns true iff every element in `buffer` satisfies `predicate`. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// - /// Buffer.forAll(buffer, func x { x > 1 }); // => true - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func forAll(buffer : Buffer, predicate : X -> Bool) : Bool { - for (element in buffer.vals()) { - if (not predicate element) { - return false - } - }; - - true - }; - - /// Returns true iff some element in `buffer` satisfies `predicate`. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// - /// Buffer.forSome(buffer, func x { x > 3 }); // => true - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func forSome(buffer : Buffer, predicate : X -> Bool) : Bool { - for (element in buffer.vals()) { - if (predicate element) { - return true - } - }; - - false - }; - - /// Returns true iff no element in `buffer` satisfies `predicate`. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// - /// Buffer.forNone(buffer, func x { x == 0 }); // => true - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func forNone(buffer : Buffer, predicate : X -> Bool) : Bool { - for (element in buffer.vals()) { - if (predicate element) { - return false - } - }; - - true - }; - - /// Creates an array containing elements from `buffer`. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// Buffer.toArray(buffer); // => [1, 2, 3] - /// - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toArray(buffer : Buffer) : [X] = - // immutable clone of array - Prim.Array_tabulate( - buffer.size(), - func(i : Nat) : X { buffer.get(i) } - ); - - /// Creates a mutable array containing elements from `buffer`. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// Buffer.toVarArray(buffer); // => [1, 2, 3] - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toVarArray(buffer : Buffer) : [var X] { - let size = buffer.size(); - if (size == 0) { [var] } else { - let newArray = Prim.Array_init(size, buffer.get(0)); - var i = 1; - while (i < size) { - newArray[i] := buffer.get(i); - i += 1 - }; - newArray - } - }; - - /// Creates a buffer containing elements from `array`. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// let array = [2, 3]; - /// - /// let buf = Buffer.fromArray(array); // => [2, 3] - /// Buffer.toText(buf, Nat.toText); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromArray(array : [X]) : Buffer { - // When returning new buffer, if possible, set the capacity - // to the capacity of the old buffer. Otherwise, return them - // at 2/3 capacity (like in this case). Alternative is to - // calculate what the size would be if the elements were - // sequentially added using `add`. This current strategy (2/3) - // is the upper bound of that calculation (if the last element - // added caused a capacity increase). - let newBuffer = Buffer(newCapacity(array.size())); - - for (element in array.vals()) { - newBuffer.add(element) - }; - - newBuffer - }; - - /// Creates a buffer containing elements from `array`. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// let array = [var 1, 2, 3]; - /// - /// let buf = Buffer.fromVarArray(array); // => [1, 2, 3] - /// Buffer.toText(buf, Nat.toText); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromVarArray(array : [var X]) : Buffer { - let newBuffer = Buffer(newCapacity(array.size())); - - for (element in array.vals()) { - newBuffer.add(element) - }; - - newBuffer - }; - - /// Creates a buffer containing elements from `iter`. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// let array = [1, 1, 1]; - /// let iter = array.vals(); - /// - /// let buf = Buffer.fromIter(iter); // => [1, 1, 1] - /// Buffer.toText(buf, Nat.toText); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromIter(iter : { next : () -> ?X }) : Buffer { - let newBuffer = Buffer(DEFAULT_CAPACITY); // can't get size from `iter` - - for (element in iter) { - newBuffer.add(element) - }; - - newBuffer - }; - - /// Reallocates the array underlying `buffer` such that capacity == size. - /// - /// Example: - /// ```motoko include=initialize - /// - /// let buffer = Buffer.Buffer(10); - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// Buffer.trimToSize(buffer); - /// buffer.capacity(); // => 3 - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func trimToSize(buffer : Buffer) { - let size = buffer.size(); - if (size < buffer.capacity()) { - buffer.reserve(size) - } - }; - - /// Creates a new buffer by applying `f` to each element in `buffer`. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// let newBuf = Buffer.map(buffer, func (x) { x + 1 }); - /// Buffer.toText(newBuf, Nat.toText); // => [2, 3, 4] - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func map(buffer : Buffer, f : X -> Y) : Buffer { - let newBuffer = Buffer(buffer.capacity()); - - for (element in buffer.vals()) { - newBuffer.add(f element) - }; - - newBuffer - }; - - /// Applies `f` to each element in `buffer`. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// import Debug "mo:base/Debug"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// Buffer.iterate(buffer, func (x) { - /// Debug.print(Nat.toText(x)); // prints each element in buffer - /// }); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func iterate(buffer : Buffer, f : X -> ()) { - for (element in buffer.vals()) { - f element - } - }; - - /// Applies `f` to each element in `buffer` and its index. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// let newBuf = Buffer.mapEntries(buffer, func (x, i) { x + i + 1 }); - /// Buffer.toText(newBuf, Nat.toText); // => [2, 4, 6] - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapEntries(buffer : Buffer, f : (Nat, X) -> Y) : Buffer { - let newBuffer = Buffer(buffer.capacity()); - - var i = 0; - let size = buffer.size(); - while (i < size) { - newBuffer.add(f(i, buffer.get(i))); - i += 1 - }; - - newBuffer - }; - - /// Creates a new buffer by applying `f` to each element in `buffer`, - /// and keeping all non-null elements. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// let newBuf = Buffer.mapFilter(buffer, func (x) { - /// if (x > 1) { - /// ?(x * 2); - /// } else { - /// null; - /// } - /// }); - /// Buffer.toText(newBuf, Nat.toText); // => [4, 6] - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapFilter(buffer : Buffer, f : X -> ?Y) : Buffer { - let newBuffer = Buffer(buffer.capacity()); - - for (element in buffer.vals()) { - switch (f element) { - case (?element) { - newBuffer.add(element) - }; - case _ {} - } - }; - - newBuffer - }; - - /// Creates a new buffer by applying `f` to each element in `buffer`. - /// If any invocation of `f` produces an `#err`, returns an `#err`. Otherwise - /// Returns an `#ok` containing the new buffer. - /// - /// Example: - /// ```motoko include=initialize - /// import Result "mo:base/Result"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// let result = Buffer.mapResult(buffer, func (k) { - /// if (k > 0) { - /// #ok(k); - /// } else { - /// #err("One or more elements are zero."); - /// } - /// }); - /// - /// Result.mapOk, [Nat], Text>(result, func buffer = Buffer.toArray(buffer)) // => #ok([1, 2, 3]) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapResult(buffer : Buffer, f : X -> Result.Result) : Result.Result, E> { - let newBuffer = Buffer(buffer.capacity()); - - for (element in buffer.vals()) { - switch (f element) { - case (#ok result) { - newBuffer.add(result) - }; - case (#err e) { - return #err e - } - } - }; - - #ok newBuffer - }; - - /// Creates a new buffer by applying `k` to each element in `buffer`, - /// and concatenating the resulting buffers in order. This operation - /// is similar to what in other functional languages is known as monadic bind. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// let chain = Buffer.chain(buffer, func (x) { - /// let b = Buffer.Buffer(2); - /// b.add(x); - /// b.add(x * 2); - /// return b; - /// }); - /// Buffer.toText(chain, Nat.toText); // => [1, 2, 2, 4, 3, 6] - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `k` runs in O(1) time and space. - public func chain(buffer : Buffer, k : X -> Buffer) : Buffer { - let newBuffer = Buffer(buffer.size() * 4); - - for (element in buffer.vals()) { - newBuffer.append(k element) - }; - - newBuffer - }; - - /// Collapses the elements in `buffer` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// Buffer.foldLeft(buffer, "", func (acc, x) { acc # Nat.toText(x)}); // => "123" - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(buffer : Buffer, base : A, combine : (A, X) -> A) : A { - var accumulation = base; - - for (element in buffer.vals()) { - accumulation := combine(accumulation, element) - }; - - accumulation - }; - - /// Collapses the elements in `buffer` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// Buffer.foldRight(buffer, "", func (x, acc) { Nat.toText(x) # acc }); // => "123" - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldRight(buffer : Buffer, base : A, combine : (X, A) -> A) : A { - let size = buffer.size(); - if (size == 0) { - return base - }; - var accumulation = base; - - var i = size; - while (i >= 1) { - i -= 1; // to avoid Nat underflow, subtract first and stop iteration at 1 - accumulation := combine(buffer.get(i), accumulation) - }; - - accumulation - }; - - /// Returns the first element of `buffer`. Traps if `buffer` is empty. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// Buffer.first(buffer); // => 1 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func first(buffer : Buffer) : X = buffer.get(0); - - /// Returns the last element of `buffer`. Traps if `buffer` is empty. - /// - /// Example: - /// ```motoko include=initialize - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// Buffer.last(buffer); // => 3 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func last(buffer : Buffer) : X = buffer.get(buffer.size() - 1); - - /// Returns a new buffer with capacity and size 1, containing `element`. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// let buffer = Buffer.make(1); - /// Buffer.toText(buffer, Nat.toText); // => [1] - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func make(element : X) : Buffer { - let newBuffer = Buffer(1); - newBuffer.add(element); - newBuffer - }; - - /// Reverses the order of elements in `buffer`. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// Buffer.reverse(buffer); - /// Buffer.toText(buffer, Nat.toText); // => [3, 2, 1] - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func reverse(buffer : Buffer) { - let size = buffer.size(); - if (size == 0) { - return - }; - - var i = 0; - var j = size - 1 : Nat; - var temp = buffer.get(0); - while (i < size / 2) { - temp := buffer.get(j); - buffer.put(j, buffer.get(i)); - buffer.put(i, temp); - i += 1; - j -= 1 - } - }; - - /// Merges two sorted buffers into a single sorted buffer, using `compare` to define - /// the ordering. The final ordering is stable. Behavior is undefined if either - /// `buffer1` or `buffer2` is not sorted. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// let buffer1 = Buffer.Buffer(2); - /// buffer1.add(1); - /// buffer1.add(2); - /// buffer1.add(4); - /// - /// let buffer2 = Buffer.Buffer(2); - /// buffer2.add(2); - /// buffer2.add(4); - /// buffer2.add(6); - /// - /// let merged = Buffer.merge(buffer1, buffer2, Nat.compare); - /// Buffer.toText(merged, Nat.toText); // => [1, 2, 2, 4, 4, 6] - /// ``` - /// - /// Runtime: O(size1 + size2) - /// - /// Space: O(size1 + size2) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func merge(buffer1 : Buffer, buffer2 : Buffer, compare : (X, X) -> Order) : Buffer { - let size1 = buffer1.size(); - let size2 = buffer2.size(); - - let newBuffer = Buffer(newCapacity(size1 + size2)); - - var pointer1 = 0; - var pointer2 = 0; - - while (pointer1 < size1 and pointer2 < size2) { - let current1 = buffer1.get(pointer1); - let current2 = buffer2.get(pointer2); - - switch (compare(current1, current2)) { - case (#less) { - newBuffer.add(current1); - pointer1 += 1 - }; - case _ { - newBuffer.add(current2); - pointer2 += 1 - } - } - }; - - while (pointer1 < size1) { - newBuffer.add(buffer1.get(pointer1)); - pointer1 += 1 - }; - - while (pointer2 < size2) { - newBuffer.add(buffer2.get(pointer2)); - pointer2 += 1 - }; - - newBuffer - }; - - /// Eliminates all duplicate elements in `buffer` as defined by `compare`. - /// Elimination is stable with respect to the original ordering of the elements. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// Buffer.removeDuplicates(buffer, Nat.compare); - /// Buffer.toText(buffer, Nat.toText); // => [1, 2, 3] - /// ``` - /// - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - public func removeDuplicates(buffer : Buffer, compare : (X, X) -> Order) { - let size = buffer.size(); - let indices = Prim.Array_tabulate<(Nat, X)>(size, func i = (i, buffer.get(i))); - // Sort based on element, while carrying original index information - // This groups together the duplicate elements - let sorted = Array.sort<(Nat, X)>(indices, func(pair1, pair2) = compare(pair1.1, pair2.1)); - let uniques = Buffer<(Nat, X)>(size); - - // Iterate over elements - var i = 0; - while (i < size) { - var j = i; - // Iterate over duplicate elements, and find the smallest index among them (for stability) - var minIndex = sorted[j]; - label duplicates while (j < (size - 1 : Nat)) { - let pair1 = sorted[j]; - let pair2 = sorted[j + 1]; - switch (compare(pair1.1, pair2.1)) { - case (#equal) { - if (pair2.0 < pair1.0) { - minIndex := pair2 - }; - j += 1 - }; - case _ { - break duplicates - } - } - }; - - uniques.add(minIndex); - i := j + 1 - }; - - // resort based on original ordering and place back in buffer - uniques.sort( - func(pair1, pair2) { - if (pair1.0 < pair2.0) { - #less - } else if (pair1.0 == pair2.0) { - #equal - } else { - #greater - } - } - ); - - buffer.clear(); - buffer.reserve(uniques.size()); - for (element in uniques.vals()) { - buffer.add(element.1) - } - }; - - /// Splits `buffer` into a pair of buffers where all elements in the left - /// buffer satisfy `predicate` and all elements in the right buffer do not. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// buffer.add(5); - /// buffer.add(6); - /// - /// let partitions = Buffer.partition(buffer, func (x) { x % 2 == 0 }); - /// (Buffer.toArray(partitions.0), Buffer.toArray(partitions.1)) // => ([2, 4, 6], [1, 3, 5]) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func partition(buffer : Buffer, predicate : X -> Bool) : (Buffer, Buffer) { - let size = buffer.size(); - let trueBuffer = Buffer(size); - let falseBuffer = Buffer(size); - - for (element in buffer.vals()) { - if (predicate element) { - trueBuffer.add(element) - } else { - falseBuffer.add(element) - } - }; - - (trueBuffer, falseBuffer) - }; - - /// Splits the buffer into two buffers at `index`, where the left buffer contains - /// all elements with indices less than `index`, and the right buffer contains all - /// elements with indices greater than or equal to `index`. Traps if `index` is out - /// of bounds. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// buffer.add(5); - /// buffer.add(6); - /// - /// let split = Buffer.split(buffer, 3); - /// (Buffer.toArray(split.0), Buffer.toArray(split.1)) // => ([1, 2, 3], [4, 5, 6]) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func split(buffer : Buffer, index : Nat) : (Buffer, Buffer) { - let size = buffer.size(); - - if (index < 0 or index > size) { - Prim.trap "Index out of bounds in split" - }; - - let buffer1 = Buffer(newCapacity index); - let buffer2 = Buffer(newCapacity(size - index)); - - var i = 0; - while (i < index) { - buffer1.add(buffer.get(i)); - i += 1 - }; - while (i < size) { - buffer2.add(buffer.get(i)); - i += 1 - }; - - (buffer1, buffer2) - }; - - /// Breaks up `buffer` into buffers of size `size`. The last chunk may - /// have less than `size` elements if the number of elements is not divisible - /// by the chunk size. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// buffer.add(4); - /// buffer.add(5); - /// buffer.add(6); - /// - /// let chunks = Buffer.chunk(buffer, 3); - /// Buffer.toText>(chunks, func buf = Buffer.toText(buf, Nat.toText)); // => [[1, 2, 3], [4, 5, 6]] - /// ``` - /// - /// Runtime: O(number of elements in buffer) - /// - /// Space: O(number of elements in buffer) - public func chunk(buffer : Buffer, size : Nat) : Buffer> { - if (size == 0) { - Prim.trap "Chunk size must be non-zero in chunk" - }; - - // ceil(buffer.size() / size) - let newBuffer = Buffer>((buffer.size() + size - 1) / size); - - var newInnerBuffer = Buffer(newCapacity size); - var innerSize = 0; - for (element in buffer.vals()) { - if (innerSize == size) { - newBuffer.add(newInnerBuffer); - newInnerBuffer := Buffer(newCapacity size); - innerSize := 0 - }; - newInnerBuffer.add(element); - innerSize += 1 - }; - if (innerSize > 0) { - newBuffer.add(newInnerBuffer) - }; - - newBuffer - }; - - /// Groups equal and adjacent elements in the list into sub lists. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(2); - /// buffer.add(4); - /// buffer.add(5); - /// buffer.add(5); - /// - /// let grouped = Buffer.groupBy(buffer, func (x, y) { x == y }); - /// Buffer.toText>(grouped, func buf = Buffer.toText(buf, Nat.toText)); // => [[1], [2, 2], [4], [5, 5]] - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func groupBy(buffer : Buffer, equal : (X, X) -> Bool) : Buffer> { - let size = buffer.size(); - let newBuffer = Buffer>(size); - if (size == 0) { - return newBuffer - }; - - var i = 0; - var baseElement = buffer.get(0); - var newInnerBuffer = Buffer(size); - while (i < size) { - let element = buffer.get(i); - - if (equal(baseElement, element)) { - newInnerBuffer.add(element) - } else { - newBuffer.add(newInnerBuffer); - baseElement := element; - newInnerBuffer := Buffer(size - i); - newInnerBuffer.add(element) - }; - i += 1 - }; - if (newInnerBuffer.size() > 0) { - newBuffer.add(newInnerBuffer) - }; - - newBuffer - }; - - /// Flattens the buffer of buffers into a single buffer. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// let buffer = Buffer.Buffer>(1); - /// - /// let inner1 = Buffer.Buffer(2); - /// inner1.add(1); - /// inner1.add(2); - /// - /// let inner2 = Buffer.Buffer(2); - /// inner2.add(3); - /// inner2.add(4); - /// - /// buffer.add(inner1); - /// buffer.add(inner2); - /// // buffer = [[1, 2], [3, 4]] - /// - /// let flat = Buffer.flatten(buffer); - /// Buffer.toText(flat, Nat.toText); // => [1, 2, 3, 4] - /// ``` - /// - /// Runtime: O(number of elements in buffer) - /// - /// Space: O(number of elements in buffer) - public func flatten(buffer : Buffer>) : Buffer { - let size = buffer.size(); - if (size == 0) { - return Buffer(0) - }; - - let newBuffer = Buffer( - if (buffer.get(0).size() != 0) { - newCapacity(buffer.get(0).size() * size) - } else { - newCapacity(size) - } - ); - - for (innerBuffer in buffer.vals()) { - for (innerElement in innerBuffer.vals()) { - newBuffer.add(innerElement) - } - }; - - newBuffer - }; - - /// Combines the two buffers into a single buffer of pairs, pairing together - /// elements with the same index. If one buffer is longer than the other, the - /// remaining elements from the longer buffer are not included. - /// - /// Example: - /// ```motoko include=initialize - /// - /// let buffer1 = Buffer.Buffer(2); - /// buffer1.add(1); - /// buffer1.add(2); - /// buffer1.add(3); - /// - /// let buffer2 = Buffer.Buffer(2); - /// buffer2.add(4); - /// buffer2.add(5); - /// - /// let zipped = Buffer.zip(buffer1, buffer2); - /// Buffer.toArray(zipped); // => [(1, 4), (2, 5)] - /// ``` - /// - /// Runtime: O(min(size1, size2)) - /// - /// Space: O(min(size1, size2)) - public func zip(buffer1 : Buffer, buffer2 : Buffer) : Buffer<(X, Y)> { - // compiler should pull lamda out as a static function since it is fully closed - zipWith(buffer1, buffer2, func(x, y) = (x, y)) - }; - - /// Combines the two buffers into a single buffer, pairing together - /// elements with the same index and combining them using `zip`. If - /// one buffer is longer than the other, the remaining elements from - /// the longer buffer are not included. - /// - /// Example: - /// ```motoko include=initialize - /// - /// let buffer1 = Buffer.Buffer(2); - /// buffer1.add(1); - /// buffer1.add(2); - /// buffer1.add(3); - /// - /// let buffer2 = Buffer.Buffer(2); - /// buffer2.add(4); - /// buffer2.add(5); - /// buffer2.add(6); - /// - /// let zipped = Buffer.zipWith(buffer1, buffer2, func (x, y) { x + y }); - /// Buffer.toArray(zipped) // => [5, 7, 9] - /// ``` - /// - /// Runtime: O(min(size1, size2)) - /// - /// Space: O(min(size1, size2)) - /// - /// *Runtime and space assumes that `zip` runs in O(1) time and space. - public func zipWith(buffer1 : Buffer, buffer2 : Buffer, zip : (X, Y) -> Z) : Buffer { - let size1 = buffer1.size(); - let size2 = buffer2.size(); - let minSize = if (size1 < size2) { size1 } else { size2 }; - - var i = 0; - let newBuffer = Buffer(newCapacity minSize); - while (i < minSize) { - newBuffer.add(zip(buffer1.get(i), buffer2.get(i))); - i += 1 - }; - newBuffer - }; - - /// Creates a new buffer taking elements in order from `buffer` until predicate - /// returns false. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// let newBuf = Buffer.takeWhile(buffer, func (x) { x < 3 }); - /// Buffer.toText(newBuf, Nat.toText); // => [1, 2] - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func takeWhile(buffer : Buffer, predicate : X -> Bool) : Buffer { - let newBuffer = Buffer(buffer.size()); - - for (element in buffer.vals()) { - if (not predicate element) { - return newBuffer - }; - newBuffer.add(element) - }; - - newBuffer - }; - - /// Creates a new buffer excluding elements in order from `buffer` until predicate - /// returns false. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// buffer.add(1); - /// buffer.add(2); - /// buffer.add(3); - /// - /// let newBuf = Buffer.dropWhile(buffer, func x { x < 3 }); // => [3] - /// Buffer.toText(newBuf, Nat.toText); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func dropWhile(buffer : Buffer, predicate : X -> Bool) : Buffer { - let size = buffer.size(); - let newBuffer = Buffer(size); - - var i = 0; - var take = false; - label iter for (element in buffer.vals()) { - if (not (take or predicate element)) { - take := true - }; - if (take) { - newBuffer.add(element) - } - }; - newBuffer - } -} diff --git a/.mops/base@0.11.1/src/CertifiedData.mo b/.mops/base@0.11.1/src/CertifiedData.mo deleted file mode 100644 index d07a962..0000000 --- a/.mops/base@0.11.1/src/CertifiedData.mo +++ /dev/null @@ -1,53 +0,0 @@ -/// Certified data. -/// -/// The Internet Computer allows canister smart contracts to store a small amount of data during -/// update method processing so that during query call processing, the canister can obtain -/// a certificate about that data. -/// -/// This module provides a _low-level_ interface to this API, aimed at advanced -/// users and library implementors. See the Internet Computer Functional -/// Specification and corresponding documentation for how to use this to make query -/// calls to your canister tamperproof. - -import Prim "mo:⛔"; - -module { - - /// Set the certified data. - /// - /// Must be called from an update method, else traps. - /// Must be passed a blob of at most 32 bytes, else traps. - /// - /// Example: - /// ```motoko no-repl - /// import CertifiedData "mo:base/CertifiedData"; - /// import Blob "mo:base/Blob"; - /// - /// // Must be in an update call - /// - /// let array : [Nat8] = [1, 2, 3]; - /// let blob = Blob.fromArray(array); - /// CertifiedData.set(blob); - /// ``` - /// - /// See a full example on how to use certified variables here: https://github.com/dfinity/examples/tree/master/motoko/cert-var - /// - public let set : (data : Blob) -> () = Prim.setCertifiedData; - - /// Gets a certificate - /// - /// Returns `null` if no certificate is available, e.g. when processing an - /// update call or inter-canister call. This returns a non-`null` value only - /// when processing a query call. - /// - /// Example: - /// ```motoko no-repl - /// import CertifiedData "mo:base/CertifiedData"; - /// // Must be in a query call - /// - /// CertifiedData.getCertificate(); - /// ``` - /// See a full example on how to use certified variables here: https://github.com/dfinity/examples/tree/master/motoko/cert-var - /// - public let getCertificate : () -> ?Blob = Prim.getCertificate; -} diff --git a/.mops/base@0.11.1/src/Char.mo b/.mops/base@0.11.1/src/Char.mo deleted file mode 100644 index d366d5e..0000000 --- a/.mops/base@0.11.1/src/Char.mo +++ /dev/null @@ -1,65 +0,0 @@ -/// Characters -import Prim "mo:⛔"; -module { - - /// Characters represented as Unicode code points. - public type Char = Prim.Types.Char; - - /// Convert character `c` to a word containing its Unicode scalar value. - public let toNat32 : (c : Char) -> Nat32 = Prim.charToNat32; - - /// Convert `w` to a character. - /// Traps if `w` is not a valid Unicode scalar value. - /// Value `w` is valid if, and only if, `w < 0xD800 or (0xE000 <= w and w <= 0x10FFFF)`. - public let fromNat32 : (w : Nat32) -> Char = Prim.nat32ToChar; - - /// Convert character `c` to single character text. - public let toText : (c : Char) -> Text = Prim.charToText; - - // Not exposed pending multi-char implementation. - private let _toUpper : (c : Char) -> Char = Prim.charToUpper; - - // Not exposed pending multi-char implementation. - private let _toLower : (c : Char) -> Char = Prim.charToLower; - - /// Returns `true` when `c` is a decimal digit between `0` and `9`, otherwise `false`. - public func isDigit(c : Char) : Bool { - Prim.charToNat32(c) -% Prim.charToNat32('0') <= (9 : Nat32) - }; - - /// Returns the Unicode _White_Space_ property of `c`. - public let isWhitespace : (c : Char) -> Bool = Prim.charIsWhitespace; - - /// Returns the Unicode _Lowercase_ property of `c`. - public let isLowercase : (c : Char) -> Bool = Prim.charIsLowercase; - - /// Returns the Unicode _Uppercase_ property of `c`. - public let isUppercase : (c : Char) -> Bool = Prim.charIsUppercase; - - /// Returns the Unicode _Alphabetic_ property of `c`. - public let isAlphabetic : (c : Char) -> Bool = Prim.charIsAlphabetic; - - /// Returns `x == y`. - public func equal(x : Char, y : Char) : Bool { x == y }; - - /// Returns `x != y`. - public func notEqual(x : Char, y : Char) : Bool { x != y }; - - /// Returns `x < y`. - public func less(x : Char, y : Char) : Bool { x < y }; - - /// Returns `x <= y`. - public func lessOrEqual(x : Char, y : Char) : Bool { x <= y }; - - /// Returns `x > y`. - public func greater(x : Char, y : Char) : Bool { x > y }; - - /// Returns `x >= y`. - public func greaterOrEqual(x : Char, y : Char) : Bool { x >= y }; - - /// Returns the order of `x` and `y`. - public func compare(x : Char, y : Char) : { #less; #equal; #greater } { - if (x < y) { #less } else if (x == y) { #equal } else { #greater } - }; - -} diff --git a/.mops/base@0.11.1/src/Debug.mo b/.mops/base@0.11.1/src/Debug.mo deleted file mode 100644 index 3e915f5..0000000 --- a/.mops/base@0.11.1/src/Debug.mo +++ /dev/null @@ -1,56 +0,0 @@ -/// Utility functions for debugging. -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Debug "mo:base/Debug"; -/// ``` - -import Prim "mo:⛔"; -module { - /// Prints `text` to output stream. - /// - /// NOTE: The output is placed in the replica log. When running on mainnet, - /// this function has no effect. - /// - /// ```motoko include=import - /// Debug.print "Hello New World!"; - /// Debug.print(debug_show(4)) // Often used with `debug_show` to convert values to Text - /// ``` - public func print(text : Text) { - Prim.debugPrint text - }; - - /// `trap(t)` traps execution with a user-provided diagnostic message. - /// - /// The caller of a future whose execution called `trap(t)` will - /// observe the trap as an `Error` value, thrown at `await`, with code - /// `#canister_error` and message `m`. Here `m` is a more descriptive `Text` - /// message derived from the provided `t`. See example for more details. - /// - /// NOTE: Other execution environments that cannot handle traps may only - /// propagate the trap and terminate execution, with or without some - /// descriptive message. - /// - /// ```motoko - /// import Debug "mo:base/Debug"; - /// import Error "mo:base/Error"; - /// - /// actor { - /// func fail() : async () { - /// Debug.trap("user provided error message"); - /// }; - /// - /// public func foo() : async () { - /// try { - /// await fail(); - /// } catch e { - /// let code = Error.code(e); // evaluates to #canister_error - /// let message = Error.message(e); // contains user provided error message - /// } - /// }; - /// } - /// ``` - public func trap(errorMessage : Text) : None { - Prim.trap errorMessage - } -} diff --git a/.mops/base@0.11.1/src/Deque.mo b/.mops/base@0.11.1/src/Deque.mo deleted file mode 100644 index 7fdeb2f..0000000 --- a/.mops/base@0.11.1/src/Deque.mo +++ /dev/null @@ -1,243 +0,0 @@ -/// Double-ended queue (deque) of a generic element type `T`. -/// -/// The interface to deques is purely functional, not imperative, and deques are immutable values. -/// In particular, deque operations such as push and pop do not update their input deque but, instead, return the -/// value of the modified deque, alongside any other data. -/// The input deque is left unchanged. -/// -/// Examples of use-cases: -/// Queue (FIFO) by using `pushBack()` and `popFront()`. -/// Stack (LIFO) by using `pushFront()` and `popFront()`. -/// -/// A deque is internally implemented as two lists, a head access list and a (reversed) tail access list, -/// that are dynamically size-balanced by splitting. -/// -/// Construction: Create a new deque with the `empty()` function. -/// -/// Note on the costs of push and pop functions: -/// * Runtime: `O(1) amortized costs, `O(n)` worst case cost per single call. -/// * Space: `O(1) amortized costs, `O(n)` worst case cost per single call. -/// -/// `n` denotes the number of elements stored in the deque. - -import List "List"; -import P "Prelude"; - -module { - type List = List.List; - - /// Double-ended queue (deque) data type. - public type Deque = (List, List); - - /// Create a new empty deque. - /// - /// Example: - /// ```motoko - /// import Deque "mo:base/Deque"; - /// - /// Deque.empty() - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func empty() : Deque { (List.nil(), List.nil()) }; - - /// Determine whether a deque is empty. - /// Returns true if `deque` is empty, otherwise `false`. - /// - /// Example: - /// ```motoko - /// import Deque "mo:base/Deque"; - /// - /// let deque = Deque.empty(); - /// Deque.isEmpty(deque) // => true - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func isEmpty(deque : Deque) : Bool { - switch deque { - case (f, r) { List.isNil(f) and List.isNil(r) } - } - }; - - func check(q : Deque) : Deque { - switch q { - case (null, r) { - let (a, b) = List.split(List.size(r) / 2, r); - (List.reverse(b), a) - }; - case (f, null) { - let (a, b) = List.split(List.size(f) / 2, f); - (a, List.reverse(b)) - }; - case q { q } - } - }; - - /// Insert a new element on the front end of a deque. - /// Returns the new deque with `element` in the front followed by the elements of `deque`. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko - /// import Deque "mo:base/Deque"; - /// - /// Deque.pushFront(Deque.pushFront(Deque.empty(), 2), 1) // deque with elements [1, 2] - /// ``` - /// - /// Runtime: `O(n)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(n)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the deque. - public func pushFront(deque : Deque, element : T) : Deque { - check(List.push(element, deque.0), deque.1) - }; - - /// Inspect the optional element on the front end of a deque. - /// Returns `null` if `deque` is empty. Otherwise, the front element of `deque`. - /// - /// Example: - /// ```motoko - /// import Deque "mo:base/Deque"; - /// - /// let deque = Deque.pushFront(Deque.pushFront(Deque.empty(), 2), 1); - /// Deque.peekFront(deque) // => ?1 - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - /// - public func peekFront(deque : Deque) : ?T { - switch deque { - case (?(x, f), r) { ?x }; - case (null, ?(x, r)) { ?x }; - case _ { null } - } - }; - - /// Remove the element on the front end of a deque. - /// Returns `null` if `deque` is empty. Otherwise, it returns a pair of - /// the first element and a new deque that contains all the remaining elements of `deque`. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko - /// import Deque "mo:base/Deque"; - /// import Debug "mo:base/Debug"; - /// let initial = Deque.pushFront(Deque.pushFront(Deque.empty(), 2), 1); - /// // initial deque with elements [1, 2] - /// let reduced = Deque.popFront(initial); - /// switch reduced { - /// case null { - /// Debug.trap "Empty queue impossible" - /// }; - /// case (?result) { - /// let removedElement = result.0; // 1 - /// let reducedDeque = result.1; // deque with element [2]. - /// } - /// } - /// ``` - /// - /// Runtime: `O(n)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(n)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the deque. - public func popFront(deque : Deque) : ?(T, Deque) { - switch deque { - case (?(x, f), r) { ?(x, check(f, r)) }; - case (null, ?(x, r)) { ?(x, check(null, r)) }; - case _ { null } - } - }; - - /// Insert a new element on the back end of a deque. - /// Returns the new deque with all the elements of `deque`, followed by `element` on the back. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko - /// import Deque "mo:base/Deque"; - /// - /// Deque.pushBack(Deque.pushBack(Deque.empty(), 1), 2) // deque with elements [1, 2] - /// ``` - /// - /// Runtime: `O(n)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(n)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the deque. - public func pushBack(deque : Deque, element : T) : Deque { - check(deque.0, List.push(element, deque.1)) - }; - - /// Inspect the optional element on the back end of a deque. - /// Returns `null` if `deque` is empty. Otherwise, the back element of `deque`. - /// - /// Example: - /// ```motoko - /// import Deque "mo:base/Deque"; - /// - /// let deque = Deque.pushBack(Deque.pushBack(Deque.empty(), 1), 2); - /// Deque.peekBack(deque) // => ?2 - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - /// - public func peekBack(deque : Deque) : ?T { - switch deque { - case (f, ?(x, r)) { ?x }; - case (?(x, r), null) { ?x }; - case _ { null } - } - }; - - /// Remove the element on the back end of a deque. - /// Returns `null` if `deque` is empty. Otherwise, it returns a pair of - /// a new deque that contains the remaining elements of `deque` - /// and, as the second pair item, the removed back element. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko - /// import Deque "mo:base/Deque"; - /// import Debug "mo:base/Debug"; - /// - /// let initial = Deque.pushBack(Deque.pushBack(Deque.empty(), 1), 2); - /// // initial deque with elements [1, 2] - /// let reduced = Deque.popBack(initial); - /// switch reduced { - /// case null { - /// Debug.trap "Empty queue impossible" - /// }; - /// case (?result) { - /// let reducedDeque = result.0; // deque with element [1]. - /// let removedElement = result.1; // 2 - /// } - /// } - /// ``` - /// - /// Runtime: `O(n)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(n)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the deque. - public func popBack(deque : Deque) : ?(Deque, T) { - switch deque { - case (f, ?(x, r)) { ?(check(f, r), x) }; - case (?(x, f), null) { ?(check(f, null), x) }; - case _ { null } - } - } -} diff --git a/.mops/base@0.11.1/src/Error.mo b/.mops/base@0.11.1/src/Error.mo deleted file mode 100644 index bf1a441..0000000 --- a/.mops/base@0.11.1/src/Error.mo +++ /dev/null @@ -1,68 +0,0 @@ -/// Error values and inspection. -/// -/// The `Error` type is the argument to `throw`, parameter of `catch`. -/// The `Error` type is opaque. - -import Prim "mo:⛔"; - -module { - - /// Error value resulting from `async` computations - public type Error = Prim.Types.Error; - - /// Error code to classify different kinds of user and system errors: - /// ```motoko - /// type ErrorCode = { - /// // Fatal error. - /// #system_fatal; - /// // Transient error. - /// #system_transient; - /// // Destination invalid. - /// #destination_invalid; - /// // Explicit reject by canister code. - /// #canister_reject; - /// // Canister trapped. - /// #canister_error; - /// // Future error code (with unrecognized numeric code). - /// #future : Nat32; - /// // Error issuing inter-canister call - /// // (indicating destination queue full or freezing threshold crossed). - /// #call_error : { err_code : Nat32 } - /// }; - /// ``` - public type ErrorCode = Prim.ErrorCode; - - /// Create an error from the message with the code `#canister_reject`. - /// - /// Example: - /// ```motoko - /// import Error "mo:base/Error"; - /// - /// Error.reject("Example error") // can be used as throw argument - /// ``` - public let reject : (message : Text) -> Error = Prim.error; - - /// Returns the code of an error. - /// - /// Example: - /// ```motoko - /// import Error "mo:base/Error"; - /// - /// let error = Error.reject("Example error"); - /// Error.code(error) // #canister_reject - /// ``` - public let code : (error : Error) -> ErrorCode = Prim.errorCode; - - /// Returns the message of an error. - /// - /// Example: - /// ```motoko - /// import Error "mo:base/Error"; - /// import Debug "mo:base/Debug"; - /// - /// let error = Error.reject("Example error"); - /// Error.message(error) // "Example error" - /// ``` - public let message : (error : Error) -> Text = Prim.errorMessage; - -} diff --git a/.mops/base@0.11.1/src/ExperimentalCycles.mo b/.mops/base@0.11.1/src/ExperimentalCycles.mo deleted file mode 100644 index 02c4f3a..0000000 --- a/.mops/base@0.11.1/src/ExperimentalCycles.mo +++ /dev/null @@ -1,151 +0,0 @@ -/// Managing cycles within actors on the Internet Computer (IC). -/// -/// The usage of the Internet Computer is measured, and paid for, in _cycles_. -/// This library provides imperative operations for observing cycles, transferring cycles, and -/// observing refunds of cycles. -/// -/// **WARNING:** This low-level API is **experimental** and likely to change or even disappear. -/// Dedicated syntactic support for manipulating cycles may be added to the language in future, obsoleting this library. -/// -/// **NOTE:** Since cycles measure computational resources, the value of `balance()` can change from one call to the next. -/// -/// Example for use on IC: -/// ```motoko no-repl -/// import Cycles "mo:base/ExperimentalCycles"; -/// import Debug "mo:base/Debug"; -/// -/// actor { -/// public func main() : async() { -/// Debug.print("Main balance: " # debug_show(Cycles.balance())); -/// Cycles.add(15_000_000); -/// await operation(); // accepts 10_000_000 cycles -/// Debug.print("Main refunded: " # debug_show(Cycles.refunded())); // 5_000_000 -/// Debug.print("Main balance: " # debug_show(Cycles.balance())); // decreased by around 10_000_000 -/// }; -/// -/// func operation() : async() { -/// Debug.print("Operation balance: " # debug_show(Cycles.balance())); -/// Debug.print("Operation available: " # debug_show(Cycles.available())); -/// let obtained = Cycles.accept(10_000_000); -/// Debug.print("Operation obtained: " # debug_show(obtained)); // => 10_000_000 -/// Debug.print("Operation balance: " # debug_show(Cycles.balance())); // increased by 10_000_000 -/// Debug.print("Operation available: " # debug_show(Cycles.available())); // decreased by 10_000_000 -/// } -/// } -/// ``` -import Prim "mo:⛔"; -module { - - /// Returns the actor's current balance of cycles as `amount`. - /// - /// Example for use on the IC: - /// ```motoko no-repl - /// import Cycles "mo:base/ExperimentalCycles"; - /// import Debug "mo:base/Debug"; - /// - /// actor { - /// public func main() : async() { - /// let balance = Cycles.balance(); - /// Debug.print("Balance: " # debug_show(balance)); - /// } - /// } - /// ``` - public let balance : () -> (amount : Nat) = Prim.cyclesBalance; - - /// Returns the currently available `amount` of cycles. - /// The amount available is the amount received in the current call, - /// minus the cumulative amount `accept`ed by this call. - /// On exit from the current shared function or async expression via `return` or `throw`, - /// any remaining available amount is automatically refunded to the caller/context. - /// - /// Example for use on the IC: - /// ```motoko no-repl - /// import Cycles "mo:base/ExperimentalCycles"; - /// import Debug "mo:base/Debug"; - /// - /// actor { - /// public func main() : async() { - /// let available = Cycles.available(); - /// Debug.print("Available: " # debug_show(available)); - /// } - /// } - /// ``` - public let available : () -> (amount : Nat) = Prim.cyclesAvailable; - - /// Transfers up to `amount` from `available()` to `balance()`. - /// Returns the amount actually transferred, which may be less than - /// requested, for example, if less is available, or if canister balance limits are reached. - /// - /// Example for use on the IC (for simplicity, only transferring cycles to itself): - /// ```motoko no-repl - /// import Cycles "mo:base/ExperimentalCycles"; - /// import Debug "mo:base/Debug"; - /// - /// actor { - /// public func main() : async() { - /// Cycles.add(15_000_000); - /// await operation(); // accepts 10_000_000 cycles - /// }; - /// - /// func operation() : async() { - /// let obtained = Cycles.accept(10_000_000); - /// Debug.print("Obtained: " # debug_show(obtained)); // => 10_000_000 - /// } - /// } - /// ``` - public let accept : (amount : Nat) -> (accepted : Nat) = Prim.cyclesAccept; - - /// Indicates additional `amount` of cycles to be transferred in - /// the next call, that is, evaluation of a shared function call or - /// async expression. - /// Traps if the current total would exceed `2 ** 128` cycles. - /// Upon the call, but not before, the total amount of cycles ``add``ed since - /// the last call is deducted from `balance()`. - /// If this total exceeds `balance()`, the caller traps, aborting the call. - /// - /// **Note**: The implicit register of added amounts is reset to zero on entry to - /// a shared function and after each shared function call or resume from an await. - /// - /// Example for use on the IC (for simplicity, only transferring cycles to itself): - /// ```motoko no-repl - /// import Cycles "mo:base/ExperimentalCycles"; - /// - /// actor { - /// func operation() : async() { - /// ignore Cycles.accept(10_000_000); - /// }; - /// - /// public func main() : async() { - /// Cycles.add(15_000_000); - /// await operation(); - /// } - /// } - /// ``` - public let add : (amount : Nat) -> () = Prim.cyclesAdd; - - /// Reports `amount` of cycles refunded in the last `await` of the current - /// context, or zero if no await has occurred yet. - /// Calling `refunded()` is solely informational and does not affect `balance()`. - /// Instead, refunds are automatically added to the current balance, - /// whether or not `refunded` is used to observe them. - /// - /// Example for use on the IC (for simplicity, only transferring cycles to itself): - /// ```motoko no-repl - /// import Cycles "mo:base/ExperimentalCycles"; - /// import Debug "mo:base/Debug"; - /// - /// actor { - /// func operation() : async() { - /// ignore Cycles.accept(10_000_000); - /// }; - /// - /// public func main() : async() { - /// Cycles.add(15_000_000); - /// await operation(); // accepts 10_000_000 cycles - /// Debug.print("Refunded: " # debug_show(Cycles.refunded())); // 5_000_000 - /// } - /// } - /// ``` - public let refunded : () -> (amount : Nat) = Prim.cyclesRefunded; - -} diff --git a/.mops/base@0.11.1/src/ExperimentalInternetComputer.mo b/.mops/base@0.11.1/src/ExperimentalInternetComputer.mo deleted file mode 100644 index bf59644..0000000 --- a/.mops/base@0.11.1/src/ExperimentalInternetComputer.mo +++ /dev/null @@ -1,85 +0,0 @@ -/// Low-level interface to the Internet Computer. -/// -/// **WARNING:** This low-level API is **experimental** and likely to change or even disappear. - -import Prim "mo:⛔"; - -module { - - /// Calls ``canister``'s update or query function, `name`, with the binary contents of `data` as IC argument. - /// Returns the response to the call, an IC _reply_ or _reject_, as a Motoko future: - /// - /// * The message data of an IC reply determines the binary contents of `reply`. - /// * The error code and textual message data of an IC reject determines the future's `Error` value. - /// - /// Note: `call` is an asynchronous function and can only be applied in an asynchronous context. - /// - /// Example: - /// ```motoko no-repl - /// import IC "mo:base/ExperimentalInternetComputer"; - /// import Principal "mo:base/Principal"; - /// - /// let ledger = Principal.fromText("ryjl3-tyaaa-aaaaa-aaaba-cai"); - /// let method = "decimals"; - /// let input = (); - /// type OutputType = { decimals : Nat32 }; - /// - /// let rawReply = await IC.call(ledger, method, to_candid(input)); // serialized Candid - /// let output : ?OutputType = from_candid(rawReply); // { decimals = 8 } - /// ``` - /// - /// [Learn more about Candid serialization](https://internetcomputer.org/docs/current/developer-docs/build/cdks/motoko-dfinity/language-manual#candid-serialization) - public let call : (canister : Principal, name : Text, data : Blob) -> async (reply : Blob) = Prim.call_raw; - - /// Given computation, `comp`, counts the number of actual and (for IC system calls) notional WebAssembly - /// instructions performed during the execution of `comp()`. - /// - /// More precisely, returns the difference between the state of the IC instruction counter (_performance counter_ `0`) before and after executing `comp()` - /// (see [Performance Counter](https://internetcomputer.org/docs/current/references/ic-interface-spec#system-api-performance-counter)). - /// - /// NB: `countInstructions(comp)` will _not_ account for any deferred garbage collection costs incurred by `comp()`. - /// - /// Example: - /// ```motoko no-repl - /// import IC "mo:base/ExperimentalInternetComputer"; - /// - /// let count = IC.countInstructions(func() { - /// // ... - /// }); - /// ``` - public func countInstructions(comp : () -> ()) : Nat64 { - let init = Prim.performanceCounter(0); - let pre = Prim.performanceCounter(0); - comp(); - let post = Prim.performanceCounter(0); - // performance_counter costs around 200 extra instructions, we perform an empty measurement to decide the overhead - let overhead = pre - init; - post - pre - overhead - }; - - /// Returns the current value of IC _performance counter_ `counter`. - /// - /// * Counter `0` is the _current execution instruction counter_, counting instructions only since the beginning of the current IC message. - /// This counter is reset to value `0` on shared function entry and every `await`. - /// It is therefore only suitable for measuring the cost of synchronous code. - /// - /// * Counter `1` is the _call context instruction counter_ for the current shared function call. - /// For replicated message executing, this excludes the cost of nested IC calls (even to the current canister). - /// For non-replicated messages, such as composite queries, it includes the cost of nested calls. - /// The current value of this counter is preserved across `awaits` (unlike counter `0`). - /// - /// * The function (currently) traps if `counter` >= 2. - /// - /// Consult [Performance Counter](https://internetcomputer.org/docs/current/references/ic-interface-spec#system-api-performance-counter) for details. - /// - /// Example: - /// ```motoko no-repl - /// import IC "mo:base/ExperimentalInternetComputer"; - /// - /// let c1 = IC.performanceCounter(1); - /// work(); - /// let diff : Nat64 = IC.performanceCounter(1) - c1; - /// ``` - public let performanceCounter : (counter : Nat32) -> (value: Nat64) = Prim.performanceCounter; - -} diff --git a/.mops/base@0.11.1/src/ExperimentalStableMemory.mo b/.mops/base@0.11.1/src/ExperimentalStableMemory.mo deleted file mode 100644 index f509994..0000000 --- a/.mops/base@0.11.1/src/ExperimentalStableMemory.mo +++ /dev/null @@ -1,353 +0,0 @@ -/// Byte-level access to (virtual) _stable memory_. -/// -/// **WARNING**: As its name suggests, this library is **experimental**, subject to change -/// and may be replaced by safer alternatives in later versions of Motoko. -/// Use at your own risk and discretion. -/// -/// **DEPRECATION**: Use of `ExperimentalStableMemory` library may be deprecated in future. -/// Going forward, users should consider using library `Region.mo` to allocate *isolated* regions of memory instead. -/// Using dedicated regions for different user applications ensures that writing -/// to one region will not affect the state of another, unrelated region. -/// -/// This is a lightweight abstraction over IC _stable memory_ and supports persisting -/// raw binary data across Motoko upgrades. -/// Use of this module is fully compatible with Motoko's use of -/// _stable variables_, whose persistence mechanism also uses (real) IC stable memory internally, but does not interfere with this API. -/// -/// Memory is allocated, using `grow(pages)`, sequentially and on demand, in units of 64KiB pages, starting with 0 allocated pages. -/// New pages are zero initialized. -/// Growth is capped by a soft limit on page count controlled by compile-time flag -/// `--max-stable-pages ` (the default is 65536, or 4GiB). -/// -/// Each `load` operation loads from byte address `offset` in little-endian -/// format using the natural bit-width of the type in question. -/// The operation traps if attempting to read beyond the current stable memory size. -/// -/// Each `store` operation stores to byte address `offset` in little-endian format using the natural bit-width of the type in question. -/// The operation traps if attempting to write beyond the current stable memory size. -/// -/// Text values can be handled by using `Text.decodeUtf8` and `Text.encodeUtf8`, in conjunction with `loadBlob` and `storeBlob`. -/// -/// The current page allocation and page contents is preserved across upgrades. -/// -/// NB: The IC's actual stable memory size (`ic0.stable_size`) may exceed the -/// page size reported by Motoko function `size()`. -/// This (and the cap on growth) are to accommodate Motoko's stable variables. -/// Applications that plan to use Motoko stable variables sparingly or not at all can -/// increase `--max-stable-pages` as desired, approaching the IC maximum (initially 8GiB, then 32Gib, currently 64Gib). -/// All applications should reserve at least one page for stable variable data, even when no stable variables are used. -/// -/// Usage: -/// ```motoko no-repl -/// import StableMemory "mo:base/ExperimentalStableMemory"; -/// ``` - -import Prim "mo:⛔"; - -module { - - /// Current size of the stable memory, in pages. - /// Each page is 64KiB (65536 bytes). - /// Initially `0`. - /// Preserved across upgrades, together with contents of allocated - /// stable memory. - /// - /// Example: - /// ```motoko no-repl - /// let beforeSize = StableMemory.size(); - /// ignore StableMemory.grow(10); - /// let afterSize = StableMemory.size(); - /// afterSize - beforeSize // => 10 - /// ``` - public let size : () -> (pages : Nat64) = Prim.stableMemorySize; - - /// Grow current `size` of stable memory by the given number of pages. - /// Each page is 64KiB (65536 bytes). - /// Returns the previous `size` when able to grow. - /// Returns `0xFFFF_FFFF_FFFF_FFFF` if remaining pages insufficient. - /// Every new page is zero-initialized, containing byte 0x00 at every offset. - /// Function `grow` is capped by a soft limit on `size` controlled by compile-time flag - /// `--max-stable-pages ` (the default is 65536, or 4GiB). - /// - /// Example: - /// ```motoko no-repl - /// import Error "mo:base/Error"; - /// - /// let beforeSize = StableMemory.grow(10); - /// if (beforeSize == 0xFFFF_FFFF_FFFF_FFFF) { - /// throw Error.reject("Out of memory"); - /// }; - /// let afterSize = StableMemory.size(); - /// afterSize - beforeSize // => 10 - /// ``` - public let grow : (newPages : Nat64) -> (oldPages : Nat64) = Prim.stableMemoryGrow; - - /// Returns a query that, when called, returns the number of bytes of (real) IC stable memory that would be - /// occupied by persisting its current stable variables before an upgrade. - /// This function may be used to monitor or limit real stable memory usage. - /// The query computes the estimate by running the first half of an upgrade, including any `preupgrade` system method. - /// Like any other query, its state changes are discarded so no actual upgrade (or other state change) takes place. - /// The query can only be called by the enclosing actor and will trap for other callers. - /// - /// Example: - /// ```motoko no-repl - /// actor { - /// stable var state = ""; - /// public func example() : async Text { - /// let memoryUsage = StableMemory.stableVarQuery(); - /// let beforeSize = (await memoryUsage()).size; - /// state #= "abcdefghijklmnopqrstuvwxyz"; - /// let afterSize = (await memoryUsage()).size; - /// debug_show (afterSize - beforeSize) - /// }; - /// }; - /// ``` - public let stableVarQuery : () -> (shared query () -> async { size : Nat64 }) = Prim.stableVarQuery; - - /// Loads a `Nat32` value from stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeNat32(offset, value); - /// StableMemory.loadNat32(offset) // => 123 - /// ``` - public let loadNat32 : (offset : Nat64) -> Nat32 = Prim.stableMemoryLoadNat32; - - /// Stores a `Nat32` value in stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeNat32(offset, value); - /// StableMemory.loadNat32(offset) // => 123 - /// ``` - public let storeNat32 : (offset : Nat64, value : Nat32) -> () = Prim.stableMemoryStoreNat32; - - /// Loads a `Nat8` value from stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeNat8(offset, value); - /// StableMemory.loadNat8(offset) // => 123 - /// ``` - public let loadNat8 : (offset : Nat64) -> Nat8 = Prim.stableMemoryLoadNat8; - - /// Stores a `Nat8` value in stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeNat8(offset, value); - /// StableMemory.loadNat8(offset) // => 123 - /// ``` - public let storeNat8 : (offset : Nat64, value : Nat8) -> () = Prim.stableMemoryStoreNat8; - - /// Loads a `Nat16` value from stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeNat16(offset, value); - /// StableMemory.loadNat16(offset) // => 123 - /// ``` - public let loadNat16 : (offset : Nat64) -> Nat16 = Prim.stableMemoryLoadNat16; - - /// Stores a `Nat16` value in stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeNat16(offset, value); - /// StableMemory.loadNat16(offset) // => 123 - /// ``` - public let storeNat16 : (offset : Nat64, value : Nat16) -> () = Prim.stableMemoryStoreNat16; - - /// Loads a `Nat64` value from stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeNat64(offset, value); - /// StableMemory.loadNat64(offset) // => 123 - /// ``` - public let loadNat64 : (offset : Nat64) -> Nat64 = Prim.stableMemoryLoadNat64; - - /// Stores a `Nat64` value in stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeNat64(offset, value); - /// StableMemory.loadNat64(offset) // => 123 - /// ``` - public let storeNat64 : (offset : Nat64, value : Nat64) -> () = Prim.stableMemoryStoreNat64; - - /// Loads an `Int32` value from stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeInt32(offset, value); - /// StableMemory.loadInt32(offset) // => 123 - /// ``` - public let loadInt32 : (offset : Nat64) -> Int32 = Prim.stableMemoryLoadInt32; - - /// Stores an `Int32` value in stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeInt32(offset, value); - /// StableMemory.loadInt32(offset) // => 123 - /// ``` - public let storeInt32 : (offset : Nat64, value : Int32) -> () = Prim.stableMemoryStoreInt32; - - /// Loads an `Int8` value from stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeInt8(offset, value); - /// StableMemory.loadInt8(offset) // => 123 - /// ``` - public let loadInt8 : (offset : Nat64) -> Int8 = Prim.stableMemoryLoadInt8; - - /// Stores an `Int8` value in stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeInt8(offset, value); - /// StableMemory.loadInt8(offset) // => 123 - /// ``` - public let storeInt8 : (offset : Nat64, value : Int8) -> () = Prim.stableMemoryStoreInt8; - - /// Loads an `Int16` value from stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeInt16(offset, value); - /// StableMemory.loadInt16(offset) // => 123 - /// ``` - public let loadInt16 : (offset : Nat64) -> Int16 = Prim.stableMemoryLoadInt16; - - /// Stores an `Int16` value in stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeInt16(offset, value); - /// StableMemory.loadInt16(offset) // => 123 - /// ``` - public let storeInt16 : (offset : Nat64, value : Int16) -> () = Prim.stableMemoryStoreInt16; - - /// Loads an `Int64` value from stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeInt64(offset, value); - /// StableMemory.loadInt64(offset) // => 123 - /// ``` - public let loadInt64 : (offset : Nat64) -> Int64 = Prim.stableMemoryLoadInt64; - - /// Stores an `Int64` value in stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 123; - /// StableMemory.storeInt64(offset, value); - /// StableMemory.loadInt64(offset) // => 123 - /// ``` - public let storeInt64 : (offset : Nat64, value : Int64) -> () = Prim.stableMemoryStoreInt64; - - /// Loads a `Float` value from stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 1.25; - /// StableMemory.storeFloat(offset, value); - /// StableMemory.loadFloat(offset) // => 1.25 - /// ``` - public let loadFloat : (offset : Nat64) -> Float = Prim.stableMemoryLoadFloat; - - /// Stores a `Float` value in stable memory at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let offset = 0; - /// let value = 1.25; - /// StableMemory.storeFloat(offset, value); - /// StableMemory.loadFloat(offset) // => 1.25 - /// ``` - public let storeFloat : (offset : Nat64, value : Float) -> () = Prim.stableMemoryStoreFloat; - - /// Load `size` bytes starting from `offset` as a `Blob`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// import Blob "mo:base/Blob"; - /// - /// let offset = 0; - /// let value = Blob.fromArray([1, 2, 3]); - /// let size = value.size(); - /// StableMemory.storeBlob(offset, value); - /// Blob.toArray(StableMemory.loadBlob(offset, size)) // => [1, 2, 3] - /// ``` - public let loadBlob : (offset : Nat64, size : Nat) -> Blob = Prim.stableMemoryLoadBlob; - - /// Write bytes of `blob` beginning at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// import Blob "mo:base/Blob"; - /// - /// let offset = 0; - /// let value = Blob.fromArray([1, 2, 3]); - /// let size = value.size(); - /// StableMemory.storeBlob(offset, value); - /// Blob.toArray(StableMemory.loadBlob(offset, size)) // => [1, 2, 3] - /// ``` - public let storeBlob : (offset : Nat64, value : Blob) -> () = Prim.stableMemoryStoreBlob; - -} diff --git a/.mops/base@0.11.1/src/Float.mo b/.mops/base@0.11.1/src/Float.mo deleted file mode 100644 index 9fb7570..0000000 --- a/.mops/base@0.11.1/src/Float.mo +++ /dev/null @@ -1,854 +0,0 @@ -/// Double precision (64-bit) floating-point numbers in IEEE 754 representation. -/// -/// This module contains common floating-point constants and utility functions. -/// -/// Notation for special values in the documentation below: -/// `+inf`: Positive infinity -/// `-inf`: Negative infinity -/// `NaN`: "not a number" (can have different sign bit values, but `NaN != NaN` regardless of the sign). -/// -/// Note: -/// Floating point numbers have limited precision and operations may inherently result in numerical errors. -/// -/// Examples of numerical errors: -/// ```motoko -/// 0.1 + 0.1 + 0.1 == 0.3 // => false -/// ``` -/// -/// ```motoko -/// 1e16 + 1.0 != 1e16 // => false -/// ``` -/// -/// (and many more cases) -/// -/// Advice: -/// * Floating point number comparisons by `==` or `!=` are discouraged. Instead, it is better to compare -/// floating-point numbers with a numerical tolerance, called epsilon. -/// -/// Example: -/// ```motoko -/// import Float "mo:base/Float"; -/// let x = 0.1 + 0.1 + 0.1; -/// let y = 0.3; -/// -/// let epsilon = 1e-6; // This depends on the application case (needs a numerical error analysis). -/// Float.equalWithin(x, y, epsilon) // => true -/// ``` -/// -/// * For absolute precision, it is recommened to encode the fraction number as a pair of a Nat for the base -/// and a Nat for the exponent (decimal point). -/// -/// NaN sign: -/// * The NaN sign is only applied by `abs`, `neg`, and `copySign`. Other operations can have an arbitrary -/// sign bit for NaN results. - -import Prim "mo:⛔"; -import Int "Int"; - -module { - - /// 64-bit floating point number type. - public type Float = Prim.Types.Float; - - /// Ratio of the circumference of a circle to its diameter. - /// Note: Limited precision. - public let pi : Float = 3.14159265358979323846; // taken from musl math.h - - /// Base of the natural logarithm. - /// Note: Limited precision. - public let e : Float = 2.7182818284590452354; // taken from musl math.h - - /// Determines whether the `number` is a `NaN` ("not a number" in the floating point representation). - /// Notes: - /// * Equality test of `NaN` with itself or another number is always `false`. - /// * There exist many internal `NaN` value representations, such as positive and negative NaN, - /// signalling and quiet NaNs, each with many different bit representations. - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.isNaN(0.0/0.0) // => true - /// ``` - public func isNaN(number : Float) : Bool { - number != number - }; - - /// Returns the absolute value of `x`. - /// - /// Special cases: - /// ``` - /// abs(+inf) => +inf - /// abs(-inf) => +inf - /// abs(-NaN) => +NaN - /// abs(-0.0) => 0.0 - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.abs(-1.2) // => 1.2 - /// ``` - public let abs : (x : Float) -> Float = Prim.floatAbs; - - /// Returns the square root of `x`. - /// - /// Special cases: - /// ``` - /// sqrt(+inf) => +inf - /// sqrt(-0.0) => -0.0 - /// sqrt(x) => NaN if x < 0.0 - /// sqrt(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.sqrt(6.25) // => 2.5 - /// ``` - public let sqrt : (x : Float) -> Float = Prim.floatSqrt; - - /// Returns the smallest integral float greater than or equal to `x`. - /// - /// Special cases: - /// ``` - /// ceil(+inf) => +inf - /// ceil(-inf) => -inf - /// ceil(NaN) => NaN - /// ceil(0.0) => 0.0 - /// ceil(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.ceil(1.2) // => 2.0 - /// ``` - public let ceil : (x : Float) -> Float = Prim.floatCeil; - - /// Returns the largest integral float less than or equal to `x`. - /// - /// Special cases: - /// ``` - /// floor(+inf) => +inf - /// floor(-inf) => -inf - /// floor(NaN) => NaN - /// floor(0.0) => 0.0 - /// floor(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.floor(1.2) // => 1.0 - /// ``` - public let floor : (x : Float) -> Float = Prim.floatFloor; - - /// Returns the nearest integral float not greater in magnitude than `x`. - /// This is equilvent to returning `x` with truncating its decimal places. - /// - /// Special cases: - /// ``` - /// trunc(+inf) => +inf - /// trunc(-inf) => -inf - /// trunc(NaN) => NaN - /// trunc(0.0) => 0.0 - /// trunc(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.trunc(2.75) // => 2.0 - /// ``` - public let trunc : (x : Float) -> Float = Prim.floatTrunc; - - /// Returns the nearest integral float to `x`. - /// A decimal place of exactly .5 is rounded up for `x > 0` - /// and rounded down for `x < 0` - /// - /// Special cases: - /// ``` - /// nearest(+inf) => +inf - /// nearest(-inf) => -inf - /// nearest(NaN) => NaN - /// nearest(0.0) => 0.0 - /// nearest(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.nearest(2.75) // => 3.0 - /// ``` - public let nearest : (x : Float) -> Float = Prim.floatNearest; - - /// Returns `x` if `x` and `y` have same sign, otherwise `x` with negated sign. - /// - /// The sign bit of zero, infinity, and `NaN` is considered. - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.copySign(1.2, -2.3) // => -1.2 - /// ``` - public let copySign : (x : Float, y : Float) -> Float = Prim.floatCopySign; - - /// Returns the smaller value of `x` and `y`. - /// - /// Special cases: - /// ``` - /// min(NaN, y) => NaN for any Float y - /// min(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.min(1.2, -2.3) // => -2.3 (with numerical imprecision) - /// ``` - public let min : (x : Float, y : Float) -> Float = Prim.floatMin; - - /// Returns the larger value of `x` and `y`. - /// - /// Special cases: - /// ``` - /// max(NaN, y) => NaN for any Float y - /// max(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.max(1.2, -2.3) // => 1.2 - /// ``` - public let max : (x : Float, y : Float) -> Float = Prim.floatMax; - - /// Returns the sine of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// sin(+inf) => NaN - /// sin(-inf) => NaN - /// sin(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.sin(Float.pi / 2) // => 1.0 - /// ``` - public let sin : (x : Float) -> Float = Prim.sin; - - /// Returns the cosine of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// cos(+inf) => NaN - /// cos(-inf) => NaN - /// cos(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.cos(Float.pi / 2) // => 0.0 (with numerical imprecision) - /// ``` - public let cos : (x : Float) -> Float = Prim.cos; - - /// Returns the tangent of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// tan(+inf) => NaN - /// tan(-inf) => NaN - /// tan(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.tan(Float.pi / 4) // => 1.0 (with numerical imprecision) - /// ``` - public let tan : (x : Float) -> Float = Prim.tan; - - /// Returns the arc sine of `x` in radians. - /// - /// Special cases: - /// ``` - /// arcsin(x) => NaN if x > 1.0 - /// arcsin(x) => NaN if x < -1.0 - /// arcsin(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.arcsin(1.0) // => Float.pi / 2 - /// ``` - public let arcsin : (x : Float) -> Float = Prim.arcsin; - - /// Returns the arc cosine of `x` in radians. - /// - /// Special cases: - /// ``` - /// arccos(x) => NaN if x > 1.0 - /// arccos(x) => NaN if x < -1.0 - /// arcos(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.arccos(1.0) // => 0.0 - /// ``` - public let arccos : (x : Float) -> Float = Prim.arccos; - - /// Returns the arc tangent of `x` in radians. - /// - /// Special cases: - /// ``` - /// arctan(+inf) => pi / 2 - /// arctan(-inf) => -pi / 2 - /// arctan(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.arctan(1.0) // => Float.pi / 4 - /// ``` - public let arctan : (x : Float) -> Float = Prim.arctan; - - /// Given `(y,x)`, returns the arc tangent in radians of `y/x` based on the signs of both values to determine the correct quadrant. - /// - /// Special cases: - /// ``` - /// arctan2(0.0, 0.0) => 0.0 - /// arctan2(-0.0, 0.0) => -0.0 - /// arctan2(0.0, -0.0) => pi - /// arctan2(-0.0, -0.0) => -pi - /// arctan2(+inf, +inf) => pi / 4 - /// arctan2(+inf, -inf) => 3 * pi / 4 - /// arctan2(-inf, +inf) => -pi / 4 - /// arctan2(-inf, -inf) => -3 * pi / 4 - /// arctan2(NaN, x) => NaN for any Float x - /// arctan2(y, NaN) => NaN for any Float y - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// let sqrt2over2 = Float.sqrt(2) / 2; - /// Float.arctan2(sqrt2over2, sqrt2over2) // => Float.pi / 4 - /// ``` - public let arctan2 : (y : Float, x : Float) -> Float = Prim.arctan2; - - /// Returns the value of `e` raised to the `x`-th power. - /// - /// Special cases: - /// ``` - /// exp(+inf) => +inf - /// exp(-inf) => 0.0 - /// exp(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.exp(1.0) // => Float.e - /// ``` - public let exp : (x : Float) -> Float = Prim.exp; - - /// Returns the natural logarithm (base-`e`) of `x`. - /// - /// Special cases: - /// ``` - /// log(0.0) => -inf - /// log(-0.0) => -inf - /// log(x) => NaN if x < 0.0 - /// log(+inf) => +inf - /// log(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.log(Float.e) // => 1.0 - /// ``` - public let log : (x : Float) -> Float = Prim.log; - - /// Formatting. `format(fmt, x)` formats `x` to `Text` according to the - /// formatting directive `fmt`, which can take one of the following forms: - /// - /// * `#fix prec` as fixed-point format with `prec` digits - /// * `#exp prec` as exponential format with `prec` digits - /// * `#gen prec` as generic format with `prec` digits - /// * `#hex prec` as hexadecimal format with `prec` digits - /// * `#exact` as exact format that can be decoded without loss. - /// - /// `-0.0` is formatted with negative sign bit. - /// Positive infinity is formatted as `inf`. - /// Negative infinity is formatted as `-inf`. - /// `NaN` is formatted as `NaN` or `-NaN` depending on its sign bit. - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.format(#exp 3, 123.0) // => "1.230e+02" - /// ``` - public func format(fmt : { #fix : Nat8; #exp : Nat8; #gen : Nat8; #hex : Nat8; #exact }, x : Float) : Text = switch fmt { - case (#fix(prec)) { Prim.floatToFormattedText(x, prec, 0) }; - case (#exp(prec)) { Prim.floatToFormattedText(x, prec, 1) }; - case (#gen(prec)) { Prim.floatToFormattedText(x, prec, 2) }; - case (#hex(prec)) { Prim.floatToFormattedText(x, prec, 3) }; - case (#exact) { Prim.floatToFormattedText(x, 17, 2) } - }; - - /// Conversion to Text. Use `format(fmt, x)` for more detailed control. - /// - /// `-0.0` is formatted with negative sign bit. - /// Positive infinity is formatted as `inf`. - /// Negative infinity is formatted as `-inf`. - /// `NaN` is formatted as `NaN` or `-NaN` depending on its sign bit. - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.toText(0.12) // => "0.12" - /// ``` - public let toText : Float -> Text = Prim.floatToText; - - /// Conversion to Int64 by truncating Float, equivalent to `toInt64(trunc(f))` - /// - /// Traps if the floating point number is larger or smaller than the representable Int64. - /// Also traps for `inf`, `-inf`, and `NaN`. - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.toInt64(-12.3) // => -12 - /// ``` - public let toInt64 : Float -> Int64 = Prim.floatToInt64; - - /// Conversion from Int64. - /// - /// Note: The floating point number may be imprecise for large or small Int64. - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.fromInt64(-42) // => -42.0 - /// ``` - public let fromInt64 : Int64 -> Float = Prim.int64ToFloat; - - /// Conversion to Int. - /// - /// Traps for `inf`, `-inf`, and `NaN`. - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.toInt(1.2e6) // => +1_200_000 - /// ``` - public let toInt : Float -> Int = Prim.floatToInt; - - /// Conversion from Int. May result in `Inf`. - /// - /// Note: The floating point number may be imprecise for large or small Int values. - /// Returns `inf` if the integer is greater than the maximum floating point number. - /// Returns `-inf` if the integer is less than the minimum floating point number. - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.fromInt(-123) // => -123.0 - /// ``` - public let fromInt : Int -> Float = Prim.intToFloat; - - /// Returns `x == y`. - /// @deprecated Use `Float.equalWithin()` as this function does not consider numerical errors. - public func equal(x : Float, y : Float) : Bool { x == y }; - - /// Returns `x != y`. - /// @deprecated Use `Float.notEqualWithin()` as this function does not consider numerical errors. - public func notEqual(x : Float, y : Float) : Bool { x != y }; - - /// Determines whether `x` is equal to `y` within the defined tolerance of `epsilon`. - /// The `epsilon` considers numerical erros, see comment above. - /// Equivalent to `Float.abs(x - y) <= epsilon` for a non-negative epsilon. - /// - /// Traps if `epsilon` is negative or `NaN`. - /// - /// Special cases: - /// ``` - /// equal(+0.0, -0.0, epsilon) => true for any `epsilon >= 0.0` - /// equal(-0.0, +0.0, epsilon) => true for any `epsilon >= 0.0` - /// equal(+inf, +inf, epsilon) => true for any `epsilon >= 0.0` - /// equal(-inf, -inf, epsilon) => true for any `epsilon >= 0.0` - /// equal(x, NaN, epsilon) => false for any x and `epsilon >= 0.0` - /// equal(NaN, y, epsilon) => false for any y and `epsilon >= 0.0` - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// let epsilon = 1e-6; - /// Float.equal(-12.3, -1.23e1, epsilon) // => true - /// ``` - public func equalWithin(x : Float, y : Float, epsilon : Float) : Bool { - if (not (epsilon >= 0.0)) { - // also considers NaN, not identical to `epsilon < 0.0` - Prim.trap("epsilon must be greater or equal 0.0") - }; - x == y or abs(x - y) <= epsilon // `x == y` to also consider infinity equal - }; - - /// Determines whether `x` is not equal to `y` within the defined tolerance of `epsilon`. - /// The `epsilon` considers numerical erros, see comment above. - /// Equivalent to `not equal(x, y, epsilon)`. - /// - /// Traps if `epsilon` is negative or `NaN`. - /// - /// Special cases: - /// ``` - /// notEqual(+0.0, -0.0, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(-0.0, +0.0, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(+inf, +inf, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(-inf, -inf, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(x, NaN, epsilon) => true for any x and `epsilon >= 0.0` - /// notEqual(NaN, y, epsilon) => true for any y and `epsilon >= 0.0` - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// let epsilon = 1e-6; - /// Float.notEqual(-12.3, -1.23e1, epsilon) // => false - /// ``` - public func notEqualWithin(x : Float, y : Float, epsilon : Float) : Bool { - not equalWithin(x, y, epsilon) - }; - - /// Returns `x < y`. - /// - /// Special cases: - /// ``` - /// less(+0.0, -0.0) => false - /// less(-0.0, +0.0) => false - /// less(NaN, y) => false for any Float y - /// less(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.less(Float.e, Float.pi) // => true - /// ``` - public func less(x : Float, y : Float) : Bool { x < y }; - - /// Returns `x <= y`. - /// - /// Special cases: - /// ``` - /// lessOrEqual(+0.0, -0.0) => true - /// lessOrEqual(-0.0, +0.0) => true - /// lessOrEqual(NaN, y) => false for any Float y - /// lessOrEqual(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.lessOrEqual(0.123, 0.1234) // => true - /// ``` - public func lessOrEqual(x : Float, y : Float) : Bool { x <= y }; - - /// Returns `x > y`. - /// - /// Special cases: - /// ``` - /// greater(+0.0, -0.0) => false - /// greater(-0.0, +0.0) => false - /// greater(NaN, y) => false for any Float y - /// greater(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.greater(Float.pi, Float.e) // => true - /// ``` - public func greater(x : Float, y : Float) : Bool { x > y }; - - /// Returns `x >= y`. - /// - /// Special cases: - /// ``` - /// greaterOrEqual(+0.0, -0.0) => true - /// greaterOrEqual(-0.0, +0.0) => true - /// greaterOrEqual(NaN, y) => false for any Float y - /// greaterOrEqual(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.greaterOrEqual(0.1234, 0.123) // => true - /// ``` - public func greaterOrEqual(x : Float, y : Float) : Bool { x >= y }; - - /// Defines a total order of `x` and `y` for use in sorting. - /// - /// Note: Using this operation to determine equality or inequality is discouraged for two reasons: - /// * It does not consider numerical errors, see comment above. Use `equal(x, y)` or - /// `notEqual(x, y)` to test for equality or inequality, respectively. - /// * `NaN` are here considered equal if their sign matches, which is different to the standard equality - /// by `==` or when using `equal()` or `notEqual()`. - /// - /// Total order: - /// * negative NaN (no distinction between signalling and quiet negative NaN) - /// * negative infinity - /// * negative numbers (including negative subnormal numbers in standard order) - /// * negative zero (`-0.0`) - /// * positive zero (`+0.0`) - /// * positive numbers (including positive subnormal numbers in standard order) - /// * positive infinity - /// * positive NaN (no distinction between signalling and quiet positive NaN) - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.compare(0.123, 0.1234) // => #less - /// ``` - public func compare(x : Float, y : Float) : { #less; #equal; #greater } { - if (isNaN(x)) { - if (isNegative(x)) { - if (isNaN(y) and isNegative(y)) { #equal } else { #less } - } else { - if (isNaN(y) and not isNegative(y)) { #equal } else { #greater } - } - } else if (isNaN(y)) { - if (isNegative(y)) { - #greater - } else { - #less - } - } else { - if (x == y) { #equal } else if (x < y) { #less } else { #greater } - } - }; - - func isNegative(number : Float) : Bool { - copySign(1.0, number) < 0.0 - }; - - /// Returns the negation of `x`, `-x` . - /// - /// Changes the sign bit for infinity. - /// - /// Special cases: - /// ``` - /// neg(+inf) => -inf - /// neg(-inf) => +inf - /// neg(+NaN) => -NaN - /// neg(-NaN) => +NaN - /// neg(+0.0) => -0.0 - /// neg(-0.0) => +0.0 - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.neg(1.23) // => -1.23 - /// ``` - public func neg(x : Float) : Float { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// add(+inf, y) => +inf if y is any Float except -inf and NaN - /// add(-inf, y) => -inf if y is any Float except +inf and NaN - /// add(+inf, -inf) => NaN - /// add(NaN, y) => NaN for any Float y - /// ``` - /// The same cases apply commutatively, i.e. for `add(y, x)`. - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.add(1.23, 0.123) // => 1.353 - /// ``` - public func add(x : Float, y : Float) : Float { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// sub(+inf, y) => +inf if y is any Float except +inf or NaN - /// sub(-inf, y) => -inf if y is any Float except -inf and NaN - /// sub(x, +inf) => -inf if x is any Float except +inf and NaN - /// sub(x, -inf) => +inf if x is any Float except -inf and NaN - /// sub(+inf, +inf) => NaN - /// sub(-inf, -inf) => NaN - /// sub(NaN, y) => NaN for any Float y - /// sub(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.sub(1.23, 0.123) // => 1.107 - /// ``` - public func sub(x : Float, y : Float) : Float { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// mul(+inf, y) => +inf if y > 0.0 - /// mul(-inf, y) => -inf if y > 0.0 - /// mul(+inf, y) => -inf if y < 0.0 - /// mul(-inf, y) => +inf if y < 0.0 - /// mul(+inf, 0.0) => NaN - /// mul(-inf, 0.0) => NaN - /// mul(NaN, y) => NaN for any Float y - /// ``` - /// The same cases apply commutatively, i.e. for `mul(y, x)`. - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.mul(1.23, 1e2) // => 123.0 - /// ``` - public func mul(x : Float, y : Float) : Float { x * y }; - - /// Returns the division of `x` by `y`, `x / y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// div(0.0, 0.0) => NaN - /// div(x, 0.0) => +inf for x > 0.0 - /// div(x, 0.0) => -inf for x < 0.0 - /// div(x, +inf) => 0.0 for any x except +inf, -inf, and NaN - /// div(x, -inf) => 0.0 for any x except +inf, -inf, and NaN - /// div(+inf, y) => +inf if y >= 0.0 - /// div(+inf, y) => -inf if y < 0.0 - /// div(-inf, y) => -inf if y >= 0.0 - /// div(-inf, y) => +inf if y < 0.0 - /// div(NaN, y) => NaN for any Float y - /// div(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.div(1.23, 1e2) // => 0.0123 - /// ``` - public func div(x : Float, y : Float) : Float { x / y }; - - /// Returns the floating point division remainder `x % y`, - /// which is defined as `x - trunc(x / y) * y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// rem(0.0, 0.0) => NaN - /// rem(x, y) => +inf if sign(x) == sign(y) for any x and y not being +inf, -inf, or NaN - /// rem(x, y) => -inf if sign(x) != sign(y) for any x and y not being +inf, -inf, or NaN - /// rem(x, +inf) => x for any x except +inf, -inf, and NaN - /// rem(x, -inf) => x for any x except +inf, -inf, and NaN - /// rem(+inf, y) => NaN for any Float y - /// rem(-inf, y) => NaN for any Float y - /// rem(NaN, y) => NaN for any Float y - /// rem(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.rem(7.2, 2.3) // => 0.3 (with numerical imprecision) - /// ``` - public func rem(x : Float, y : Float) : Float { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// pow(+inf, y) => +inf for any y > 0.0 including +inf - /// pow(+inf, 0.0) => 1.0 - /// pow(+inf, y) => 0.0 for any y < 0.0 including -inf - /// pow(x, +inf) => +inf if x > 0.0 or x < 0.0 - /// pow(0.0, +inf) => 0.0 - /// pow(x, -inf) => 0.0 if x > 0.0 or x < 0.0 - /// pow(0.0, -inf) => +inf - /// pow(x, y) => NaN if x < 0.0 and y is a non-integral Float - /// pow(-inf, y) => +inf if y > 0.0 and y is a non-integral or an even integral Float - /// pow(-inf, y) => -inf if y > 0.0 and y is an odd integral Float - /// pow(-inf, 0.0) => 1.0 - /// pow(-inf, y) => 0.0 if y < 0.0 - /// pow(-inf, +inf) => +inf - /// pow(-inf, -inf) => 1.0 - /// pow(NaN, y) => NaN if y != 0.0 - /// pow(NaN, 0.0) => 1.0 - /// pow(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko - /// import Float "mo:base/Float"; - /// - /// Float.pow(2.5, 2.0) // => 6.25 - /// ``` - public func pow(x : Float, y : Float) : Float { x ** y }; - -} diff --git a/.mops/base@0.11.1/src/Func.mo b/.mops/base@0.11.1/src/Func.mo deleted file mode 100644 index 17a352d..0000000 --- a/.mops/base@0.11.1/src/Func.mo +++ /dev/null @@ -1,46 +0,0 @@ -/// Functions on functions, creating functions from simpler inputs. -/// -/// (Most commonly used when programming in functional style using higher-order -/// functions.) - -module { - /// Import from the base library to use this module. - /// - /// ```motoko name=import - /// import { compose; const; identity } = "mo:base/Func"; - /// import Text = "mo:base/Text"; - /// import Char = "mo:base/Char"; - /// ``` - - /// The composition of two functions `f` and `g` is a function that applies `g` and then `f`. - /// - /// Example: - /// ```motoko include=import - /// let textFromNat32 = compose(Text.fromChar, Char.fromNat32); - /// assert textFromNat32(65) == "A"; - /// ``` - public func compose(f : B -> C, g : A -> B) : A -> C { - func(x : A) : C { - f(g(x)) - } - }; - - /// The `identity` function returns its argument. - /// Example: - /// ```motoko include=import - /// assert identity(10) == 10; - /// assert identity(true) == true; - /// ``` - public func identity(x : A) : A = x; - - /// The const function is a _curried_ function that accepts an argument `x`, - /// and then returns a function that discards its argument and always returns - /// the `x`. - /// - /// Example: - /// ```motoko include=import - /// assert const(10)("hello") == 10; - /// assert const(true)(20) == true; - /// ``` - public func const(x : A) : B -> A = func _ = x -} diff --git a/.mops/base@0.11.1/src/Hash.mo b/.mops/base@0.11.1/src/Hash.mo deleted file mode 100644 index 285c02b..0000000 --- a/.mops/base@0.11.1/src/Hash.mo +++ /dev/null @@ -1,82 +0,0 @@ -/// Hash values - -import Prim "mo:⛔"; -import Iter "Iter"; - -module { - - /// Hash values represent a string of _hash bits_, packed into a `Nat32`. - public type Hash = Nat32; - - /// The hash length, always 31. - public let length : Nat = 31; // Why not 32? - - /// Project a given bit from the bit vector. - public func bit(h : Hash, pos : Nat) : Bool { - assert (pos <= length); - (h & (Prim.natToNat32(1) << Prim.natToNat32(pos))) != Prim.natToNat32(0) - }; - - /// Test if two hashes are equal - public func equal(ha : Hash, hb : Hash) : Bool { - ha == hb - }; - - /// Computes a hash from the least significant 32-bits of `n`, ignoring other bits. - /// @deprecated For large `Nat` values consider using a bespoke hash function that considers all of the argument's bits. - public func hash(n : Nat) : Hash { - let j = Prim.intToNat32Wrap(n); - hashNat8([ - j & (255 << 0), - j & (255 << 8), - j & (255 << 16), - j & (255 << 24) - ]) - }; - - /// @deprecated This function will be removed in future. - public func debugPrintBits(bits : Hash) { - for (j in Iter.range(0, length - 1)) { - if (bit(bits, j)) { - Prim.debugPrint("1") - } else { - Prim.debugPrint("0") - } - } - }; - - /// @deprecated This function will be removed in future. - public func debugPrintBitsRev(bits : Hash) { - for (j in Iter.revRange(length - 1, 0)) { - if (bit(bits, Prim.abs(j))) { - Prim.debugPrint("1") - } else { - Prim.debugPrint("0") - } - } - }; - - /// Jenkin's one at a time: - /// - /// https://en.wikipedia.org/wiki/Jenkins_hash_function#one_at_a_time - /// - /// The input type should actually be `[Nat8]`. - /// Note: Be sure to explode each `Nat8` of a `Nat32` into its own `Nat32`, and to shift into lower 8 bits. - - // should this really be public? - // NB: Int.mo contains a local copy of hashNat8 (redefined to suppress the deprecation warning). - /// @deprecated This function may be removed or changed in future. - public func hashNat8(key : [Hash]) : Hash { - var hash : Nat32 = 0; - for (natOfKey in key.vals()) { - hash := hash +% natOfKey; - hash := hash +% hash << 10; - hash := hash ^ (hash >> 6) - }; - hash := hash +% hash << 3; - hash := hash ^ (hash >> 11); - hash := hash +% hash << 15; - return hash - }; - -} diff --git a/.mops/base@0.11.1/src/HashMap.mo b/.mops/base@0.11.1/src/HashMap.mo deleted file mode 100644 index 39cabbb..0000000 --- a/.mops/base@0.11.1/src/HashMap.mo +++ /dev/null @@ -1,457 +0,0 @@ -/// Class `HashMap` provides a hashmap from keys of type `K` to values of type `V`. - -/// The class is parameterized by the key's equality and hash functions, -/// and an initial capacity. However, the underlying allocation happens only when -/// the first key-value entry is inserted. -/// -/// Internally, the map is represented as an array of `AssocList` (buckets). -/// The growth policy of the underyling array is very simple, for now: double -/// the current capacity when the expected bucket list size grows beyond a -/// certain constant. -/// -/// WARNING: Certain operations are amortized O(1) time, such as `put`, but run -/// in worst case O(size) time. These worst case runtimes may exceed the cycles limit -/// per message if the size of the map is large enough. Further, this runtime analysis -/// assumes that the hash functions uniformly maps keys over the hash space. Grow these structures -/// with discretion, and with good hash functions. All amortized operations -/// below also list the worst case runtime. -/// -/// For maps without amortization, see `TrieMap`. -/// -/// Note on the constructor: -/// The argument `initCapacity` determines the initial number of buckets in the -/// underyling array. Also, the runtime and space anlyses in this documentation -/// assumes that the equality and hash functions for keys used to construct the -/// map run in O(1) time and space. -/// -/// Example: -/// ```motoko name=initialize -/// import HashMap "mo:base/HashMap"; -/// import Text "mo:base/Text"; -/// -/// let map = HashMap.HashMap(5, Text.equal, Text.hash); -/// ``` -/// -/// Runtime: O(1) -/// -/// Space: O(1) - -import Prim "mo:⛔"; -import P "Prelude"; -import A "Array"; -import Hash "Hash"; -import Iter "Iter"; -import AssocList "AssocList"; -import Nat32 "Nat32"; - -module { - - // hash field avoids re-hashing the key when the array grows. - type Key = (Hash.Hash, K); - - // key-val list type - type KVs = AssocList.AssocList, V>; - - public class HashMap( - initCapacity : Nat, - keyEq : (K, K) -> Bool, - keyHash : K -> Hash.Hash - ) { - - var table : [var KVs] = [var]; - var _count : Nat = 0; - - /// Returns the current number of key-value entries in the map. - /// - /// Example: - /// ```motoko include=initialize - /// map.size() // => 0 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func size() : Nat = _count; - - /// Returns the value assocaited with key `key` if present and `null` otherwise. - /// - /// Example: - /// ```motoko include=initialize - /// map.put("key", 3); - /// map.get("key") // => ?3 - /// ``` - /// - /// Expected Runtime: O(1), Worst Case Runtime: O(size) - /// - /// Space: O(1) - public func get(key : K) : (value : ?V) { - let h = Prim.nat32ToNat(keyHash(key)); - let m = table.size(); - if (m > 0) { - AssocList.find, V>(table[h % m], keyHash_(key), keyHashEq) - } else { - null - } - }; - - /// Insert the value `value` with key `key`. Overwrites any existing entry with key `key`. - /// - /// Example: - /// ```motoko include=initialize - /// map.put("key", 3); - /// map.get("key") // => ?3 - /// ``` - /// - /// Expected Amortized Runtime: O(1), Worst Case Runtime: O(size) - /// - /// Expected Amortized Space: O(1), Worst Case Space: O(size) - /// - /// Note: If this is the first entry into this map, this operation will cause - /// the initial allocation of the underlying array. - public func put(key : K, value : V) = ignore replace(key, value); - - /// Insert the value `value` with key `key`. Returns the previous value - /// associated with key `key` or `null` if no such value exists. - /// - /// Example: - /// ```motoko include=initialize - /// map.put("key", 3); - /// ignore map.replace("key", 2); // => ?3 - /// map.get("key") // => ?2 - /// ``` - /// - /// Expected Amortized Runtime: O(1), Worst Case Runtime: O(size) - /// - /// Expected Amortized Space: O(1), Worst Case Space: O(size) - /// - /// Note: If this is the first entry into this map, this operation will cause - /// the initial allocation of the underlying array. - public func replace(key : K, value : V) : (oldValue : ?V) { - if (_count >= table.size()) { - let size = if (_count == 0) { - if (initCapacity > 0) { - initCapacity - } else { - 1 - } - } else { - table.size() * 2 - }; - let table2 = A.init>(size, null); - for (i in table.keys()) { - var kvs = table[i]; - label moveKeyVals : () loop { - switch kvs { - case null { break moveKeyVals }; - case (?((k, v), kvsTail)) { - let pos2 = Nat32.toNat(k.0) % table2.size(); // critical: uses saved hash. no re-hash. - table2[pos2] := ?((k, v), table2[pos2]); - kvs := kvsTail - } - } - } - }; - table := table2 - }; - let h = Prim.nat32ToNat(keyHash(key)); - let pos = h % table.size(); - let (kvs2, ov) = AssocList.replace, V>(table[pos], keyHash_(key), keyHashEq, ?value); - table[pos] := kvs2; - switch (ov) { - case null { _count += 1 }; - case _ {} - }; - ov - }; - - /// Deletes the entry with the key `key`. Has no effect if `key` is not - /// present in the map. - /// - /// Example: - /// ```motoko include=initialize - /// map.put("key", 3); - /// map.delete("key"); - /// map.get("key"); // => null - /// ``` - /// - /// Expected Runtime: O(1), Worst Case Runtime: O(size) - /// - /// Expected Space: O(1), Worst Case Space: O(size) - public func delete(key : K) = ignore remove(key); - - func keyHash_(k : K) : Key = (keyHash(k), k); - - func keyHashEq(k1 : Key, k2 : Key) : Bool { - k1.0 == k2.0 and keyEq(k1.1, k2.1) - }; - - /// Deletes the entry with the key `key`. Returns the previous value - /// associated with key `key` or `null` if no such value exists. - /// - /// Example: - /// ```motoko include=initialize - /// map.put("key", 3); - /// map.remove("key"); // => ?3 - /// ``` - /// - /// Expected Runtime: O(1), Worst Case Runtime: O(size) - /// - /// Expected Space: O(1), Worst Case Space: O(size) - public func remove(key : K) : (oldValue : ?V) { - let m = table.size(); - if (m > 0) { - let h = Prim.nat32ToNat(keyHash(key)); - let pos = h % m; - let (kvs2, ov) = AssocList.replace, V>(table[pos], keyHash_(key), keyHashEq, null); - table[pos] := kvs2; - switch (ov) { - case null {}; - case _ { _count -= 1 } - }; - ov - } else { - null - } - }; - - /// Returns an Iterator (`Iter`) over the keys of the map. - /// Iterator provides a single method `next()`, which returns - /// keys in no specific order, or `null` when out of keys to iterate over. - /// - /// Example: - /// ```motoko include=initialize - /// - /// map.put("key1", 1); - /// map.put("key2", 2); - /// map.put("key3", 3); - /// - /// var keys = ""; - /// for (key in map.keys()) { - /// keys := key # " " # keys - /// }; - /// keys // => "key3 key2 key1 " - /// ``` - /// - /// Cost of iteration over all keys: - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func keys() : Iter.Iter { - Iter.map(entries(), func(kv : (K, V)) : K { kv.0 }) - }; - - /// Returns an Iterator (`Iter`) over the values of the map. - /// Iterator provides a single method `next()`, which returns - /// values in no specific order, or `null` when out of values to iterate over. - /// - /// Example: - /// ```motoko include=initialize - /// - /// map.put("key1", 1); - /// map.put("key2", 2); - /// map.put("key3", 3); - /// - /// var sum = 0; - /// for (value in map.vals()) { - /// sum += value; - /// }; - /// sum // => 6 - /// ``` - /// - /// Cost of iteration over all values: - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func vals() : Iter.Iter { - Iter.map(entries(), func(kv : (K, V)) : V { kv.1 }) - }; - - /// Returns an Iterator (`Iter`) over the key-value pairs in the map. - /// Iterator provides a single method `next()`, which returns - /// pairs in no specific order, or `null` when out of pairs to iterate over. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// map.put("key1", 1); - /// map.put("key2", 2); - /// map.put("key3", 3); - /// - /// var pairs = ""; - /// for ((key, value) in map.entries()) { - /// pairs := "(" # key # ", " # Nat.toText(value) # ") " # pairs - /// }; - /// pairs // => "(key3, 3) (key2, 2) (key1, 1)" - /// ``` - /// - /// Cost of iteration over all pairs: - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func entries() : Iter.Iter<(K, V)> { - if (table.size() == 0) { - object { public func next() : ?(K, V) { null } } - } else { - object { - var kvs = table[0]; - var nextTablePos = 1; - public func next() : ?(K, V) { - switch kvs { - case (?(kv, kvs2)) { - kvs := kvs2; - ?(kv.0.1, kv.1) - }; - case null { - if (nextTablePos < table.size()) { - kvs := table[nextTablePos]; - nextTablePos += 1; - next() - } else { - null - } - } - } - } - } - } - }; - - }; - - /// Returns a copy of `map`, initializing the copy with the provided equality - /// and hash functions. - /// - /// Example: - /// ```motoko include=initialize - /// map.put("key1", 1); - /// map.put("key2", 2); - /// map.put("key3", 3); - /// - /// let map2 = HashMap.clone(map, Text.equal, Text.hash); - /// map2.get("key1") // => ?1 - /// ``` - /// - /// Expected Runtime: O(size), Worst Case Runtime: O(size * size) - /// - /// Expected Space: O(size), Worst Case Space: O(size) - public func clone( - map : HashMap, - keyEq : (K, K) -> Bool, - keyHash : K -> Hash.Hash - ) : HashMap { - let h2 = HashMap(map.size(), keyEq, keyHash); - for ((k, v) in map.entries()) { - h2.put(k, v) - }; - h2 - }; - - /// Returns a new map, containing all entries given by the iterator `iter`. - /// The new map is initialized with the provided initial capacity, equality, - /// and hash functions. - /// - /// Example: - /// ```motoko include=initialize - /// let entries = [("key3", 3), ("key2", 2), ("key1", 1)]; - /// let iter = entries.vals(); - /// - /// let map2 = HashMap.fromIter(iter, entries.size(), Text.equal, Text.hash); - /// map2.get("key1") // => ?1 - /// ``` - /// - /// Expected Runtime: O(size), Worst Case Runtime: O(size * size) - /// - /// Expected Space: O(size), Worst Case Space: O(size) - public func fromIter( - iter : Iter.Iter<(K, V)>, - initCapacity : Nat, - keyEq : (K, K) -> Bool, - keyHash : K -> Hash.Hash - ) : HashMap { - let h = HashMap(initCapacity, keyEq, keyHash); - for ((k, v) in iter) { - h.put(k, v) - }; - h - }; - - /// Creates a new map by applying `f` to each entry in `hashMap`. Each entry - /// `(k, v)` in the old map is transformed into a new entry `(k, v2)`, where - /// the new value `v2` is created by applying `f` to `(k, v)`. - /// - /// ```motoko include=initialize - /// map.put("key1", 1); - /// map.put("key2", 2); - /// map.put("key3", 3); - /// - /// let map2 = HashMap.map(map, Text.equal, Text.hash, func (k, v) = v * 2); - /// map2.get("key2") // => ?4 - /// ``` - /// - /// Expected Runtime: O(size), Worst Case Runtime: O(size * size) - /// - /// Expected Space: O(size), Worst Case Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func map( - hashMap : HashMap, - keyEq : (K, K) -> Bool, - keyHash : K -> Hash.Hash, - f : (K, V1) -> V2 - ) : HashMap { - let h2 = HashMap(hashMap.size(), keyEq, keyHash); - for ((k, v1) in hashMap.entries()) { - let v2 = f(k, v1); - h2.put(k, v2) - }; - h2 - }; - - /// Creates a new map by applying `f` to each entry in `hashMap`. For each entry - /// `(k, v)` in the old map, if `f` evaluates to `null`, the entry is discarded. - /// Otherwise, the entry is transformed into a new entry `(k, v2)`, where - /// the new value `v2` is the result of applying `f` to `(k, v)`. - /// - /// ```motoko include=initialize - /// map.put("key1", 1); - /// map.put("key2", 2); - /// map.put("key3", 3); - /// - /// let map2 = - /// HashMap.mapFilter( - /// map, - /// Text.equal, - /// Text.hash, - /// func (k, v) = if (v == 2) { null } else { ?(v * 2)} - /// ); - /// map2.get("key3") // => ?6 - /// ``` - /// - /// Expected Runtime: O(size), Worst Case Runtime: O(size * size) - /// - /// Expected Space: O(size), Worst Case Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapFilter( - hashMap : HashMap, - keyEq : (K, K) -> Bool, - keyHash : K -> Hash.Hash, - f : (K, V1) -> ?V2 - ) : HashMap { - let h2 = HashMap(hashMap.size(), keyEq, keyHash); - for ((k, v1) in hashMap.entries()) { - switch (f(k, v1)) { - case null {}; - case (?v2) { - h2.put(k, v2) - } - } - }; - h2 - }; - -} diff --git a/.mops/base@0.11.1/src/Heap.mo b/.mops/base@0.11.1/src/Heap.mo deleted file mode 100644 index de326e2..0000000 --- a/.mops/base@0.11.1/src/Heap.mo +++ /dev/null @@ -1,233 +0,0 @@ -/// Class `Heap` provides a priority queue of elements of type `X`. -/// -/// The class wraps a purely-functional implementation based on a leftist heap. -/// -/// Note on the constructor: -/// The constructor takes in a comparison function `compare` that defines the -/// ordering between elements of type `X`. Most primitive types have a default -/// version of this comparison function defined in their modules (e.g. `Nat.compare`). -/// The runtime analysis in this documentation assumes that the `compare` function -/// runs in `O(1)` time and space. -/// -/// Example: -/// ```motoko name=initialize -/// import Heap "mo:base/Heap"; -/// import Text "mo:base/Text"; -/// -/// let heap = Heap.Heap(Text.compare); -/// ``` -/// -/// Runtime: `O(1)` -/// -/// Space: `O(1)` - -import O "Order"; -import P "Prelude"; -import L "List"; -import I "Iter"; - -module { - - public type Tree = ?(Int, X, Tree, Tree); - - public class Heap(compare : (X, X) -> O.Order) { - var heap : Tree = null; - - /// Inserts an element into the heap. - /// - /// Example: - /// ```motoko include=initialize - /// - /// heap.put("apple"); - /// heap.peekMin() // => ?"apple" - /// ``` - /// - /// Runtime: `O(log(n))` - /// - /// Space: `O(log(n))` - public func put(x : X) { - heap := merge(heap, ?(1, x, null, null), compare) - }; - - /// Return the minimal element in the heap, or `null` if the heap is empty. - /// - /// Example: - /// ```motoko include=initialize - /// - /// heap.put("apple"); - /// heap.put("banana"); - /// heap.put("cantaloupe"); - /// heap.peekMin() // => ?"apple" - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func peekMin() : ?X { - switch heap { - case (null) { null }; - case (?(_, x, _, _)) { ?x } - } - }; - - /// Delete the minimal element in the heap, if it exists. - /// - /// Example: - /// ```motoko include=initialize - /// - /// heap.put("apple"); - /// heap.put("banana"); - /// heap.put("cantaloupe"); - /// heap.deleteMin(); - /// heap.peekMin(); // => ?"banana" - /// ``` - /// - /// Runtime: `O(log(n))` - /// - /// Space: `O(log(n))` - public func deleteMin() { - switch heap { - case null {}; - case (?(_, _, a, b)) { heap := merge(a, b, compare) } - } - }; - - /// Delete and return the minimal element in the heap, if it exists. - /// - /// Example: - /// ```motoko include=initialize - /// - /// heap.put("apple"); - /// heap.put("banana"); - /// heap.put("cantaloupe"); - /// heap.removeMin(); // => ?"apple" - /// ``` - /// - /// Runtime: `O(log(n))` - /// - /// Space: `O(log(n))` - public func removeMin() : (minElement : ?X) { - switch heap { - case null { null }; - case (?(_, x, a, b)) { - heap := merge(a, b, compare); - ?x - } - } - }; - - /// Return a snapshot of the internal functional tree representation as sharable data. - /// The returned tree representation is not affected by subsequent changes of the `Heap` instance. - /// - /// Example: - /// ```motoko include=initialize - /// - /// heap.put("banana"); - /// heap.share(); - /// ``` - /// - /// Useful for storing the heap as a stable variable, pretty-printing, and sharing it across async function calls, - /// i.e. passing it in async arguments or async results. - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func share() : Tree { - heap - }; - - /// Rewraps a snapshot of a heap (obtained by `share()`) in a `Heap` instance. - /// The wrapping instance must be initialized with the same `compare` - /// function that created the snapshot. - /// - /// Example: - /// ```motoko include=initialize - /// - /// heap.put("apple"); - /// heap.put("banana"); - /// let snapshot = heap.share(); - /// let heapCopy = Heap.Heap(Text.compare); - /// heapCopy.unsafeUnshare(snapshot); - /// heapCopy.peekMin() // => ?"apple" - /// ``` - /// - /// Useful for loading a stored heap from a stable variable or accesing a heap - /// snapshot passed from an async function call. - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func unsafeUnshare(tree : Tree) { - heap := tree - }; - - }; - - func rank(heap : Tree) : Int { - switch heap { - case null { 0 }; - case (?(r, _, _, _)) { r } - } - }; - - func makeT(x : X, a : Tree, b : Tree) : Tree { - if (rank(a) >= rank(b)) { - ?(rank(b) + 1, x, a, b) - } else { - ?(rank(a) + 1, x, b, a) - } - }; - - func merge(h1 : Tree, h2 : Tree, compare : (X, X) -> O.Order) : Tree { - switch (h1, h2) { - case (null, h) { h }; - case (h, null) { h }; - case (?(_, x, a, b), ?(_, y, c, d)) { - switch (compare(x, y)) { - case (#less) { makeT(x, a, merge(b, h2, compare)) }; - case _ { makeT(y, c, merge(d, h1, compare)) } - } - } - } - }; - - /// Returns a new `Heap`, containing all entries given by the iterator `iter`. - /// The new map is initialized with the provided `compare` function. - /// - /// Example: - /// ```motoko include=initialize - /// let entries = ["banana", "apple", "cantaloupe"]; - /// let iter = entries.vals(); - /// - /// let newHeap = Heap.fromIter(iter, Text.compare); - /// newHeap.peekMin() // => ?"apple" - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func fromIter(iter : I.Iter, compare : (X, X) -> O.Order) : Heap { - let heap = Heap(compare); - func build(xs : L.List>) : Tree { - func join(xs : L.List>) : L.List> { - switch (xs) { - case (null) { null }; - case (?(hd, null)) { ?(hd, null) }; - case (?(h1, ?(h2, tl))) { ?(merge(h1, h2, compare), join(tl)) } - } - }; - switch (xs) { - case null { P.unreachable() }; - case (?(hd, null)) { hd }; - case _ { build(join(xs)) } - } - }; - let list = I.toList(I.map(iter, func(x : X) : Tree { ?(1, x, null, null) })); - if (not L.isNil(list)) { - let t = build(list); - heap.unsafeUnshare(t) - }; - heap - }; - -} diff --git a/.mops/base@0.11.1/src/Int.mo b/.mops/base@0.11.1/src/Int.mo deleted file mode 100644 index 0dd479c..0000000 --- a/.mops/base@0.11.1/src/Int.mo +++ /dev/null @@ -1,370 +0,0 @@ -/// Signed integer numbers with infinite precision (also called big integers). -/// -/// Most operations on integer numbers (e.g. addition) are available as built-in operators (e.g. `-1 + 1`). -/// This module provides equivalent functions and `Text` conversion. -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Int "mo:base/Int"; -/// ``` - -import Prim "mo:⛔"; -import Prelude "Prelude"; -import Hash "Hash"; - -module { - - /// Infinite precision signed integers. - public type Int = Prim.Types.Int; - - /// Returns the absolute value of `x`. - /// - /// Example: - /// ```motoko include=import - /// Int.abs(-12) // => 12 - /// ``` - public func abs(x : Int) : Nat { - Prim.abs(x) - }; - - /// Converts an integer number to its textual representation. Textual - /// representation _do not_ contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// Int.toText(-1234) // => "-1234" - /// ``` - public func toText(x : Int) : Text { - if (x == 0) { - return "0" - }; - - let isNegative = x < 0; - var int = if isNegative { -x } else { x }; - - var text = ""; - let base = 10; - - while (int > 0) { - let rem = int % base; - text := ( - switch (rem) { - case 0 { "0" }; - case 1 { "1" }; - case 2 { "2" }; - case 3 { "3" }; - case 4 { "4" }; - case 5 { "5" }; - case 6 { "6" }; - case 7 { "7" }; - case 8 { "8" }; - case 9 { "9" }; - case _ { Prelude.unreachable() } - } - ) # text; - int := int / base - }; - - return if isNegative { "-" # text } else { text } - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Int.min(2, -3) // => -3 - /// ``` - public func min(x : Int, y : Int) : Int { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Int.max(2, -3) // => 2 - /// ``` - public func max(x : Int, y : Int) : Int { - if (x < y) { y } else { x } - }; - - // this is a local copy of deprecated Hash.hashNat8 (redefined to suppress the warning) - private func hashNat8(key : [Nat32]) : Hash.Hash { - var hash : Nat32 = 0; - for (natOfKey in key.vals()) { - hash := hash +% natOfKey; - hash := hash +% hash << 10; - hash := hash ^ (hash >> 6) - }; - hash := hash +% hash << 3; - hash := hash ^ (hash >> 11); - hash := hash +% hash << 15; - return hash - }; - - /// Computes a hash from the least significant 32-bits of `i`, ignoring other bits. - /// @deprecated For large `Int` values consider using a bespoke hash function that considers all of the argument's bits. - public func hash(i : Int) : Hash.Hash { - // CAUTION: This removes the high bits! - let j = Prim.int32ToNat32(Prim.intToInt32Wrap(i)); - hashNat8([ - j & (255 << 0), - j & (255 << 8), - j & (255 << 16), - j & (255 << 24) - ]) - }; - - /// Computes an accumulated hash from `h1` and the least significant 32-bits of `i`, ignoring other bits in `i`. - /// @deprecated For large `Int` values consider using a bespoke hash function that considers all of the argument's bits. - public func hashAcc(h1 : Hash.Hash, i : Int) : Hash.Hash { - // CAUTION: This removes the high bits! - let j = Prim.int32ToNat32(Prim.intToInt32Wrap(i)); - hashNat8([ - h1, - j & (255 << 0), - j & (255 << 8), - j & (255 << 16), - j & (255 << 24) - ]) - }; - - /// Equality function for Int types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// Int.equal(-1, -1); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Buffer "mo:base/Buffer"; - /// - /// let buffer1 = Buffer.Buffer(1); - /// buffer1.add(-3); - /// let buffer2 = Buffer.Buffer(1); - /// buffer2.add(-3); - /// Buffer.equal(buffer1, buffer2, Int.equal) // => true - /// ``` - public func equal(x : Int, y : Int) : Bool { x == y }; - - /// Inequality function for Int types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// Int.notEqual(-1, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int, y : Int) : Bool { x != y }; - - /// "Less than" function for Int types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// Int.less(-2, 1); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int, y : Int) : Bool { x < y }; - - /// "Less than or equal" function for Int types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// Int.lessOrEqual(-2, 1); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int, y : Int) : Bool { x <= y }; - - /// "Greater than" function for Int types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// Int.greater(1, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int, y : Int) : Bool { x > y }; - - /// "Greater than or equal" function for Int types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// Int.greaterOrEqual(1, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int, y : Int) : Bool { x >= y }; - - /// General-purpose comparison function for `Int`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// Int.compare(-3, 2) // => #less - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.sort([1, -2, -3], Int.compare) // => [-3, -2, 1] - /// ``` - public func compare(x : Int, y : Int) : { #less; #equal; #greater } { - if (x < y) { #less } else if (x == y) { #equal } else { #greater } - }; - - /// Returns the negation of `x`, `-x` . - /// - /// Example: - /// ```motoko include=import - /// Int.neg(123) // => -123 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int) : Int { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// Int.add(1, -2); // => -1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 0, Int.add) // => -4 - /// ``` - public func add(x : Int, y : Int) : Int { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// Int.sub(1, 2); // => -1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 0, Int.sub) // => 4 - /// ``` - public func sub(x : Int, y : Int) : Int { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// Int.mul(-2, 3); // => -6 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 1, Int.mul) // => 6 - /// ``` - public func mul(x : Int, y : Int) : Int { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// Int.div(6, -2); // => -3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int, y : Int) : Int { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// Int.rem(6, -4); // => 2 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int, y : Int) : Int { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps when `y` is negative or `y > 2 ** 32 - 1`. - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// Int.pow(-2, 3); // => -8 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int, y : Int) : Int { x ** y }; - -} diff --git a/.mops/base@0.11.1/src/Int16.mo b/.mops/base@0.11.1/src/Int16.mo deleted file mode 100644 index aa83787..0000000 --- a/.mops/base@0.11.1/src/Int16.mo +++ /dev/null @@ -1,641 +0,0 @@ -/// Provides utility functions on 16-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Int16 "mo:base/Int16"; -/// ``` -import Int "Int"; -import Prim "mo:⛔"; - -module { - - /// 16-bit signed integers. - public type Int16 = Prim.Types.Int16; - - /// Minimum 16-bit integer value, `-2 ** 15`. - /// - /// Example: - /// ```motoko include=import - /// Int16.minimumValue // => -32_768 : Int16 - /// ``` - public let minimumValue = -32_768 : Int16; - - /// Maximum 16-bit integer value, `+2 ** 15 - 1`. - /// - /// Example: - /// ```motoko include=import - /// Int16.maximumValue // => +32_767 : Int16 - /// ``` - public let maximumValue = 32_767 : Int16; - - /// Converts a 16-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// Int16.toInt(12_345) // => 12_345 : Int - /// ``` - public let toInt : Int16 -> Int = Prim.int16ToInt; - - /// Converts a signed integer with infinite precision to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int16.fromInt(12_345) // => +12_345 : Int16 - /// ``` - public let fromInt : Int -> Int16 = Prim.intToInt16; - - /// Converts a signed integer with infinite precision to a 16-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int16.fromIntWrap(-12_345) // => -12_345 : Int - /// ``` - public let fromIntWrap : Int -> Int16 = Prim.intToInt16Wrap; - - /// Converts a 8-bit signed integer to a 16-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// Int16.fromInt8(-123) // => -123 : Int16 - /// ``` - public let fromInt8 : Int8 -> Int16 = Prim.int8ToInt16; - - /// Converts a 16-bit signed integer to a 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int16.toInt8(-123) // => -123 : Int8 - /// ``` - public let toInt8 : Int16 -> Int8 = Prim.int16ToInt8; - - /// Converts a 32-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int16.fromInt32(-12_345) // => -12_345 : Int16 - /// ``` - public let fromInt32 : Int32 -> Int16 = Prim.int32ToInt16; - - /// Converts a 16-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// Int16.toInt32(-12_345) // => -12_345 : Int32 - /// ``` - public let toInt32 : Int16 -> Int32 = Prim.int16ToInt32; - - /// Converts an unsigned 16-bit integer to a signed 16-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int16.fromNat16(12_345) // => +12_345 : Int16 - /// ``` - public let fromNat16 : Nat16 -> Int16 = Prim.nat16ToInt16; - - /// Converts a signed 16-bit integer to an unsigned 16-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int16.toNat16(-1) // => 65_535 : Nat16 // underflow - /// ``` - public let toNat16 : Int16 -> Nat16 = Prim.int16ToNat16; - - /// Returns the Text representation of `x`. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// Int16.toText(-12345) // => "-12345" - /// ``` - public func toText(x : Int16) : Text { - Int.toText(toInt(x)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 15` (the minimum `Int16` value). - /// - /// Example: - /// ```motoko include=import - /// Int16.abs(-12345) // => +12_345 - /// ``` - public func abs(x : Int16) : Int16 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Int16.min(+2, -3) // => -3 - /// ``` - public func min(x : Int16, y : Int16) : Int16 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Int16.max(+2, -3) // => +2 - /// ``` - public func max(x : Int16, y : Int16) : Int16 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int16 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// Int16.equal(-1, -1); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Buffer "mo:base/Buffer"; - /// - /// let buffer1 = Buffer.Buffer(1); - /// buffer1.add(-3); - /// let buffer2 = Buffer.Buffer(1); - /// buffer2.add(-3); - /// Buffer.equal(buffer1, buffer2, Int16.equal) // => true - /// ``` - public func equal(x : Int16, y : Int16) : Bool { x == y }; - - /// Inequality function for Int16 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// Int16.notEqual(-1, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int16, y : Int16) : Bool { x != y }; - - /// "Less than" function for Int16 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// Int16.less(-2, 1); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int16, y : Int16) : Bool { x < y }; - - /// "Less than or equal" function for Int16 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// Int16.lessOrEqual(-2, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int16, y : Int16) : Bool { x <= y }; - - /// "Greater than" function for Int16 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// Int16.greater(-2, 1); // => false - /// ``` - public func greater(x : Int16, y : Int16) : Bool { x > y }; - - /// "Greater than or equal" function for Int16 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// Int16.greaterOrEqual(-2, -2); // => true - /// ``` - public func greaterOrEqual(x : Int16, y : Int16) : Bool { x >= y }; - - /// General-purpose comparison function for `Int16`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// Int16.compare(-3, 2) // => #less - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.sort([1, -2, -3] : [Int16], Int16.compare) // => [-3, -2, 1] - /// ``` - public func compare(x : Int16, y : Int16) : { #less; #equal; #greater } { - if (x < y) { #less } else if (x == y) { #equal } else { #greater } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 15)`. - /// - /// Example: - /// ```motoko include=import - /// Int16.neg(123) // => -123 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int16) : Int16 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int16.add(100, 23) // => +123 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 0, Int16.add) // => -4 - /// ``` - public func add(x : Int16, y : Int16) : Int16 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int16.sub(123, 100) // => +23 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 0, Int16.sub) // => 4 - /// ``` - public func sub(x : Int16, y : Int16) : Int16 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int16.mul(12, 10) // => +120 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 1, Int16.mul) // => 6 - /// ``` - public func mul(x : Int16, y : Int16) : Int16 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// Int16.div(123, 10) // => +12 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int16, y : Int16) : Int16 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// Int16.rem(123, 10) // => +3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int16, y : Int16) : Int16 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 16`. - /// - /// Example: - /// ```motoko include=import - /// Int16.pow(2, 10) // => +1_024 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int16, y : Int16) : Int16 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitnot(-256 /* 0xff00 */) // => +255 // 0xff - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int16) : Int16 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitand(0x0fff, 0x00f0) // => +240 // 0xf0 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int16, y : Int16) : Int16 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitor(0x0f0f, 0x00f0) // => +4_095 // 0x0fff - /// ``` - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int16, y : Int16) : Int16 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitxor(0x0fff, 0x00f0) // => +3_855 // 0x0f0f - /// ``` - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int16, y : Int16) : Int16 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 16`, the semantics is the same as for `bitshiftLeft(x, y % 16)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitshiftLeft(1, 8) // => +256 // 0x100 equivalent to `2 ** 8`. - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int16, y : Int16) : Int16 { x << y }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 16`, the semantics is the same as for `bitshiftRight(x, y % 16)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitshiftRight(1024, 8) // => +4 // equivalent to `1024 / (2 ** 8)` - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int16, y : Int16) : Int16 { x >> y }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like other bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 16`, the semantics is the same as for `bitrotLeft(x, y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitrotLeft(0x2001, 4) // => +18 // 0x12. - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int16, y : Int16) : Int16 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like other bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 16`, the semantics is the same as for `bitrotRight(x, y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitrotRight(0x2010, 8) // => +4_128 // 0x01020. - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int16, y : Int16) : Int16 { x <>> y }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 16`, the semantics is the same as for `bittest(x, p % 16)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// Int16.bittest(128, 7) // => true - /// ``` - public func bittest(x : Int16, p : Nat) : Bool { - Prim.btstInt16(x, Prim.intToInt16(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 16`, the semantics is the same as for `bitset(x, p % 16)`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitset(0, 7) // => +128 - /// ``` - public func bitset(x : Int16, p : Nat) : Int16 { - x | (1 << Prim.intToInt16(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 16`, the semantics is the same as for `bitclear(x, p % 16)`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitclear(-1, 7) // => -129 - /// ``` - public func bitclear(x : Int16, p : Nat) : Int16 { - x & ^(1 << Prim.intToInt16(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 16`, the semantics is the same as for `bitclear(x, p % 16)`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitflip(255, 7) // => +127 - /// ``` - public func bitflip(x : Int16, p : Nat) : Int16 { - x ^ (1 << Prim.intToInt16(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitcountNonZero(0xff) // => +8 - /// ``` - public let bitcountNonZero : (x : Int16) -> Int16 = Prim.popcntInt16; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitcountLeadingZero(0x80) // => +8 - /// ``` - public let bitcountLeadingZero : (x : Int16) -> Int16 = Prim.clzInt16; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Int16.bitcountTrailingZero(0x0100) // => +8 - /// ``` - public let bitcountTrailingZero : (x : Int16) -> Int16 = Prim.ctzInt16; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int16.addWrap(2 ** 14, 2 ** 14) // => -32_768 // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int16, y : Int16) : Int16 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int16.subWrap(-2 ** 15, 1) // => +32_767 // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int16, y : Int16) : Int16 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int16.mulWrap(2 ** 8, 2 ** 8) // => 0 // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int16, y : Int16) : Int16 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 16`. - /// - /// Example: - /// ```motoko include=import - /// - /// Int16.powWrap(2, 15) // => -32_768 // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int16, y : Int16) : Int16 { x **% y } -} diff --git a/.mops/base@0.11.1/src/Int32.mo b/.mops/base@0.11.1/src/Int32.mo deleted file mode 100644 index 30f0661..0000000 --- a/.mops/base@0.11.1/src/Int32.mo +++ /dev/null @@ -1,653 +0,0 @@ -/// Provides utility functions on 32-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Int32 "mo:base/Int32"; -/// ``` -import Int "Int"; -import Prim "mo:⛔"; - -module { - - /// 32-bit signed integers. - public type Int32 = Prim.Types.Int32; - - /// Minimum 32-bit integer value, `-2 ** 31`. - /// - /// Example: - /// ```motoko include=import - /// Int32.minimumValue // => -2_147_483_648 - /// ``` - public let minimumValue = -2_147_483_648 : Int32; - - /// Maximum 32-bit integer value, `+2 ** 31 - 1`. - /// - /// Example: - /// ```motoko include=import - /// Int32.maximumValue // => +2_147_483_647 - /// ``` - public let maximumValue = 2_147_483_647 : Int32; - - /// Converts a 32-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// Int32.toInt(123_456) // => 123_456 : Int - /// ``` - public let toInt : Int32 -> Int = Prim.int32ToInt; - - /// Converts a signed integer with infinite precision to a 32-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int32.fromInt(123_456) // => +123_456 : Int32 - /// ``` - public let fromInt : Int -> Int32 = Prim.intToInt32; - - /// Converts a signed integer with infinite precision to a 32-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int32.fromIntWrap(-123_456) // => -123_456 : Int - /// ``` - public let fromIntWrap : Int -> Int32 = Prim.intToInt32Wrap; - - /// Converts a 16-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// Int32.fromInt16(-123) // => -123 : Int32 - /// ``` - public let fromInt16 : Int16 -> Int32 = Prim.int16ToInt32; - - /// Converts a 32-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int32.toInt16(-123) // => -123 : Int16 - /// ``` - public let toInt16 : Int32 -> Int16 = Prim.int32ToInt16; - - /// Converts a 64-bit signed integer to a 32-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int32.fromInt64(-123_456) // => -123_456 : Int32 - /// ``` - public let fromInt64 : Int64 -> Int32 = Prim.int64ToInt32; - - /// Converts a 32-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// Int32.toInt64(-123_456) // => -123_456 : Int64 - /// ``` - public let toInt64 : Int32 -> Int64 = Prim.int32ToInt64; - - /// Converts an unsigned 32-bit integer to a signed 32-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int32.fromNat32(123_456) // => +123_456 : Int32 - /// ``` - public let fromNat32 : Nat32 -> Int32 = Prim.nat32ToInt32; - - /// Converts a signed 32-bit integer to an unsigned 32-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int32.toNat32(-1) // => 4_294_967_295 : Nat32 // underflow - /// ``` - public let toNat32 : Int32 -> Nat32 = Prim.int32ToNat32; - - /// Returns the Text representation of `x`. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// Int32.toText(-123456) // => "-123456" - /// ``` - public func toText(x : Int32) : Text { - Int.toText(toInt(x)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 31` (the minimum `Int32` value). - /// - /// Example: - /// ```motoko include=import - /// Int32.abs(-123456) // => +123_456 - /// ``` - public func abs(x : Int32) : Int32 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Int32.min(+2, -3) // => -3 - /// ``` - public func min(x : Int32, y : Int32) : Int32 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Int32.max(+2, -3) // => +2 - /// ``` - public func max(x : Int32, y : Int32) : Int32 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int32 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// Int32.equal(-1, -1); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Buffer "mo:base/Buffer"; - /// - /// let buffer1 = Buffer.Buffer(1); - /// buffer1.add(-3); - /// let buffer2 = Buffer.Buffer(1); - /// buffer2.add(-3); - /// Buffer.equal(buffer1, buffer2, Int32.equal) // => true - /// ``` - public func equal(x : Int32, y : Int32) : Bool { x == y }; - - /// Inequality function for Int32 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// Int32.notEqual(-1, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int32, y : Int32) : Bool { x != y }; - - /// "Less than" function for Int32 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// Int32.less(-2, 1); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int32, y : Int32) : Bool { x < y }; - - /// "Less than or equal" function for Int32 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// Int32.lessOrEqual(-2, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int32, y : Int32) : Bool { x <= y }; - - /// "Greater than" function for Int32 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// Int32.greater(-2, -3); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int32, y : Int32) : Bool { x > y }; - - /// "Greater than or equal" function for Int32 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// Int32.greaterOrEqual(-2, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int32, y : Int32) : Bool { x >= y }; - - /// General-purpose comparison function for `Int32`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// Int32.compare(-3, 2) // => #less - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.sort([1, -2, -3] : [Int32], Int32.compare) // => [-3, -2, 1] - /// ``` - public func compare(x : Int32, y : Int32) : { #less; #equal; #greater } { - if (x < y) { #less } else if (x == y) { #equal } else { #greater } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 31)`. - /// - /// Example: - /// ```motoko include=import - /// Int32.neg(123) // => -123 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int32) : Int32 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int32.add(100, 23) // => +123 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 0, Int32.add) // => -4 - /// ``` - public func add(x : Int32, y : Int32) : Int32 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int32.sub(1234, 123) // => +1_111 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 0, Int32.sub) // => 6 - /// ``` - public func sub(x : Int32, y : Int32) : Int32 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int32.mul(123, 100) // => +12_300 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 1, Int32.mul) // => 6 - /// ``` - public func mul(x : Int32, y : Int32) : Int32 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// Int32.div(123, 10) // => +12 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int32, y : Int32) : Int32 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// Int32.rem(123, 10) // => +3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int32, y : Int32) : Int32 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 32`. - /// - /// Example: - /// ```motoko include=import - /// Int32.pow(2, 10) // => +1_024 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int32, y : Int32) : Int32 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitnot(-256 /* 0xffff_ff00 */) // => +255 // 0xff - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int32) : Int32 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitand(0xffff, 0x00f0) // => +240 // 0xf0 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int32, y : Int32) : Int32 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitor(0xffff, 0x00f0) // => +65_535 // 0xffff - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int32, y : Int32) : Int32 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitxor(0xffff, 0x00f0) // => +65_295 // 0xff0f - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int32, y : Int32) : Int32 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 32`, the semantics is the same as for `bitshiftLeft(x, y % 32)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitshiftLeft(1, 8) // => +256 // 0x100 equivalent to `2 ** 8`. - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int32, y : Int32) : Int32 { x << y }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 32`, the semantics is the same as for `bitshiftRight(x, y % 32)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitshiftRight(1024, 8) // => +4 // equivalent to `1024 / (2 ** 8)` - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int32, y : Int32) : Int32 { x >> y }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like other bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 32`, the semantics is the same as for `bitrotLeft(x, y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitrotLeft(0x2000_0001, 4) // => +18 // 0x12. - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int32, y : Int32) : Int32 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like other bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 32`, the semantics is the same as for `bitrotRight(x, y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitrotRight(0x0002_0001, 8) // => +16_777_728 // 0x0100_0200. - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int32, y : Int32) : Int32 { x <>> y }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 32`, the semantics is the same as for `bittest(x, p % 32)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// Int32.bittest(128, 7) // => true - /// ``` - public func bittest(x : Int32, p : Nat) : Bool { - Prim.btstInt32(x, Prim.intToInt32(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 32`, the semantics is the same as for `bitset(x, p % 32)`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitset(0, 7) // => +128 - /// ``` - public func bitset(x : Int32, p : Nat) : Int32 { - x | (1 << Prim.intToInt32(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 32`, the semantics is the same as for `bitclear(x, p % 32)`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitclear(-1, 7) // => -129 - /// ``` - public func bitclear(x : Int32, p : Nat) : Int32 { - x & ^(1 << Prim.intToInt32(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 32`, the semantics is the same as for `bitclear(x, p % 32)`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitflip(255, 7) // => +127 - /// ``` - public func bitflip(x : Int32, p : Nat) : Int32 { - x ^ (1 << Prim.intToInt32(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitcountNonZero(0xffff) // => +16 - /// ``` - public let bitcountNonZero : (x : Int32) -> Int32 = Prim.popcntInt32; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitcountLeadingZero(0x8000) // => +16 - /// ``` - public let bitcountLeadingZero : (x : Int32) -> Int32 = Prim.clzInt32; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Int32.bitcountTrailingZero(0x0201_0000) // => +16 - /// ``` - public let bitcountTrailingZero : (x : Int32) -> Int32 = Prim.ctzInt32; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int32.addWrap(2 ** 30, 2 ** 30) // => -2_147_483_648 // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int32, y : Int32) : Int32 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int32.subWrap(-2 ** 31, 1) // => +2_147_483_647 // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int32, y : Int32) : Int32 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int32.mulWrap(2 ** 16, 2 ** 16) // => 0 // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int32, y : Int32) : Int32 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 32`. - /// - /// Example: - /// ```motoko include=import - /// Int32.powWrap(2, 31) // => -2_147_483_648 // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int32, y : Int32) : Int32 { x **% y }; - -} diff --git a/.mops/base@0.11.1/src/Int64.mo b/.mops/base@0.11.1/src/Int64.mo deleted file mode 100644 index 171760e..0000000 --- a/.mops/base@0.11.1/src/Int64.mo +++ /dev/null @@ -1,639 +0,0 @@ -/// Provides utility functions on 64-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Int64 "mo:base/Int64"; -/// ``` - -import Int "Int"; -import Prim "mo:⛔"; - -module { - - /// 64-bit signed integers. - public type Int64 = Prim.Types.Int64; - - /// Minimum 64-bit integer value, `-2 ** 63`. - /// - /// Example: - /// ```motoko include=import - /// Int64.minimumValue // => -9_223_372_036_854_775_808 - /// ``` - public let minimumValue = -9_223_372_036_854_775_808 : Int64; - - /// Maximum 64-bit integer value, `+2 ** 63 - 1`. - /// - /// Example: - /// ```motoko include=import - /// Int64.maximumValue // => +9_223_372_036_854_775_807 - /// ``` - public let maximumValue = 9_223_372_036_854_775_807 : Int64; - - /// Converts a 64-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// Int64.toInt(123_456) // => 123_456 : Int - /// ``` - public let toInt : Int64 -> Int = Prim.int64ToInt; - - /// Converts a signed integer with infinite precision to a 64-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int64.fromInt(123_456) // => +123_456 : Int64 - /// ``` - public let fromInt : Int -> Int64 = Prim.intToInt64; - - /// Converts a 32-bit signed integer to a 64-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int64.fromInt32(-123_456) // => -123_456 : Int64 - /// ``` - public let fromInt32 : Int32 -> Int64 = Prim.int32ToInt64; - - /// Converts a 64-bit signed integer to a 32-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int64.toInt32(-123_456) // => -123_456 : Int32 - /// ``` - public let toInt32 : Int64 -> Int32 = Prim.int64ToInt32; - - /// Converts a signed integer with infinite precision to a 64-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int64.fromIntWrap(-123_456) // => -123_456 : Int64 - /// ``` - public let fromIntWrap : Int -> Int64 = Prim.intToInt64Wrap; - - /// Converts an unsigned 64-bit integer to a signed 64-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int64.fromNat64(123_456) // => +123_456 : Int64 - /// ``` - public let fromNat64 : Nat64 -> Int64 = Prim.nat64ToInt64; - - /// Converts a signed 64-bit integer to an unsigned 64-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int64.toNat64(-1) // => 18_446_744_073_709_551_615 : Nat64 // underflow - /// ``` - public let toNat64 : Int64 -> Nat64 = Prim.int64ToNat64; - - /// Returns the Text representation of `x`. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// - /// Example: - /// ```motoko include=import - /// Int64.toText(-123456) // => "-123456" - /// ``` - public func toText(x : Int64) : Text { - Int.toText(toInt(x)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 63` (the minimum `Int64` value). - /// - /// Example: - /// ```motoko include=import - /// Int64.abs(-123456) // => +123_456 - /// ``` - public func abs(x : Int64) : Int64 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Int64.min(+2, -3) // => -3 - /// ``` - public func min(x : Int64, y : Int64) : Int64 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Int64.max(+2, -3) // => +2 - /// ``` - public func max(x : Int64, y : Int64) : Int64 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int64 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// Int64.equal(-1, -1); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Buffer "mo:base/Buffer"; - /// - /// let buffer1 = Buffer.Buffer(1); - /// buffer1.add(-3); - /// let buffer2 = Buffer.Buffer(1); - /// buffer2.add(-3); - /// Buffer.equal(buffer1, buffer2, Int64.equal) // => true - /// ``` - public func equal(x : Int64, y : Int64) : Bool { x == y }; - - /// Inequality function for Int64 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// Int64.notEqual(-1, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int64, y : Int64) : Bool { x != y }; - - /// "Less than" function for Int64 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// Int64.less(-2, 1); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int64, y : Int64) : Bool { x < y }; - - /// "Less than or equal" function for Int64 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// Int64.lessOrEqual(-2, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int64, y : Int64) : Bool { x <= y }; - - /// "Greater than" function for Int64 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// Int64.greater(-2, -3); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int64, y : Int64) : Bool { x > y }; - - /// "Greater than or equal" function for Int64 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// Int64.greaterOrEqual(-2, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int64, y : Int64) : Bool { x >= y }; - - /// General-purpose comparison function for `Int64`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// Int64.compare(-3, 2) // => #less - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.sort([1, -2, -3] : [Int64], Int64.compare) // => [-3, -2, 1] - /// ``` - public func compare(x : Int64, y : Int64) : { #less; #equal; #greater } { - if (x < y) { #less } else if (x == y) { #equal } else { #greater } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 63)`. - /// - /// Example: - /// ```motoko include=import - /// Int64.neg(123) // => -123 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int64) : Int64 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int64.add(1234, 123) // => +1_357 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 0, Int64.add) // => -4 - /// ``` - public func add(x : Int64, y : Int64) : Int64 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int64.sub(123, 100) // => +23 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 0, Int64.sub) // => 4 - /// ``` - public func sub(x : Int64, y : Int64) : Int64 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int64.mul(123, 10) // => +1_230 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 1, Int64.mul) // => 6 - /// ``` - public func mul(x : Int64, y : Int64) : Int64 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// Int64.div(123, 10) // => +12 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int64, y : Int64) : Int64 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// Int64.rem(123, 10) // => +3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int64, y : Int64) : Int64 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 64`. - /// - /// Example: - /// ```motoko include=import - /// Int64.pow(2, 10) // => +1_024 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int64, y : Int64) : Int64 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// Int64.bitnot(-256 /* 0xffff_ffff_ffff_ff00 */) // => +255 // 0xff - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int64) : Int64 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// Int64.bitand(0xffff, 0x00f0) // => +240 // 0xf0 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int64, y : Int64) : Int64 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// Int64.bitor(0xffff, 0x00f0) // => +65_535 // 0xffff - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int64, y : Int64) : Int64 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// Int64.bitxor(0xffff, 0x00f0) // => +65_295 // 0xff0f - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int64, y : Int64) : Int64 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 64`, the semantics is the same as for `bitshiftLeft(x, y % 64)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// Int64.bitshiftLeft(1, 8) // => +256 // 0x100 equivalent to `2 ** 8`. - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int64, y : Int64) : Int64 { x << y }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 64`, the semantics is the same as for `bitshiftRight(x, y % 64)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// Int64.bitshiftRight(1024, 8) // => +4 // equivalent to `1024 / (2 ** 8)` - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int64, y : Int64) : Int64 { x >> y }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like other bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 64`, the semantics is the same as for `bitrotLeft(x, y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// - /// Int64.bitrotLeft(0x2000_0000_0000_0001, 4) // => +18 // 0x12. - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int64, y : Int64) : Int64 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like other bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 64`, the semantics is the same as for `bitrotRight(x, y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// Int64.bitrotRight(0x0002_0000_0000_0001, 48) // => +65538 // 0x1_0002. - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int64, y : Int64) : Int64 { x <>> y }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 64`, the semantics is the same as for `bittest(x, p % 64)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// Int64.bittest(128, 7) // => true - /// ``` - public func bittest(x : Int64, p : Nat) : Bool { - Prim.btstInt64(x, Prim.intToInt64(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 64`, the semantics is the same as for `bitset(x, p % 64)`. - /// - /// Example: - /// ```motoko include=import - /// Int64.bitset(0, 7) // => +128 - /// ``` - public func bitset(x : Int64, p : Nat) : Int64 { - x | (1 << Prim.intToInt64(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 64`, the semantics is the same as for `bitclear(x, p % 64)`. - /// - /// Example: - /// ```motoko include=import - /// Int64.bitclear(-1, 7) // => -129 - /// ``` - public func bitclear(x : Int64, p : Nat) : Int64 { - x & ^(1 << Prim.intToInt64(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 64`, the semantics is the same as for `bitclear(x, p % 64)`. - /// - /// Example: - /// ```motoko include=import - /// Int64.bitflip(255, 7) // => +127 - /// ``` - public func bitflip(x : Int64, p : Nat) : Int64 { - x ^ (1 << Prim.intToInt64(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Int64.bitcountNonZero(0xffff) // => +16 - /// ``` - public let bitcountNonZero : (x : Int64) -> Int64 = Prim.popcntInt64; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Int64.bitcountLeadingZero(0x8000_0000) // => +32 - /// ``` - public let bitcountLeadingZero : (x : Int64) -> Int64 = Prim.clzInt64; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Int64.bitcountTrailingZero(0x0201_0000) // => +16 - /// ``` - public let bitcountTrailingZero : (x : Int64) -> Int64 = Prim.ctzInt64; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int64.addWrap(2 ** 62, 2 ** 62) // => -9_223_372_036_854_775_808 // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int64, y : Int64) : Int64 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int64.subWrap(-2 ** 63, 1) // => +9_223_372_036_854_775_807 // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int64, y : Int64) : Int64 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int64.mulWrap(2 ** 32, 2 ** 32) // => 0 // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int64, y : Int64) : Int64 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 64`. - /// - /// Example: - /// ```motoko include=import - /// Int64.powWrap(2, 63) // => -9_223_372_036_854_775_808 // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int64, y : Int64) : Int64 { x **% y } -} diff --git a/.mops/base@0.11.1/src/Int8.mo b/.mops/base@0.11.1/src/Int8.mo deleted file mode 100644 index 2c8d4d8..0000000 --- a/.mops/base@0.11.1/src/Int8.mo +++ /dev/null @@ -1,634 +0,0 @@ -/// Provides utility functions on 8-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Int8 "mo:base/Int8"; -/// ``` -import Int "Int"; -import Prim "mo:⛔"; - -module { - - /// 8-bit signed integers. - public type Int8 = Prim.Types.Int8; - - /// Minimum 8-bit integer value, `-2 ** 7`. - /// - /// Example: - /// ```motoko include=import - /// Int8.minimumValue // => -128 - /// ``` - public let minimumValue = -128 : Int8; - - /// Maximum 8-bit integer value, `+2 ** 7 - 1`. - /// - /// Example: - /// ```motoko include=import - /// Int8.maximumValue // => +127 - /// ``` - public let maximumValue = 127 : Int8; - - /// Converts an 8-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// Int8.toInt(123) // => 123 : Int - /// ``` - public let toInt : Int8 -> Int = Prim.int8ToInt; - - /// Converts a signed integer with infinite precision to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int8.fromInt(123) // => +123 : Int8 - /// ``` - public let fromInt : Int -> Int8 = Prim.intToInt8; - - /// Converts a signed integer with infinite precision to an 8-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int8.fromIntWrap(-123) // => -123 : Int - /// ``` - public let fromIntWrap : Int -> Int8 = Prim.intToInt8Wrap; - - /// Converts a 16-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int8.fromInt16(123) // => +123 : Int8 - /// ``` - public let fromInt16 : Int16 -> Int8 = Prim.int16ToInt8; - - /// Converts an 8-bit signed integer to a 16-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// Int8.toInt16(123) // => +123 : Int16 - /// ``` - public let toInt16 : Int8 -> Int16 = Prim.int8ToInt16; - - /// Converts an unsigned 8-bit integer to a signed 8-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int8.fromNat8(123) // => +123 : Int8 - /// ``` - public let fromNat8 : Nat8 -> Int8 = Prim.nat8ToInt8; - - /// Converts a signed 8-bit integer to an unsigned 8-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int8.toNat8(-1) // => 255 : Nat8 // underflow - /// ``` - public let toNat8 : Int8 -> Nat8 = Prim.int8ToNat8; - - /// Converts an integer number to its textual representation. - /// - /// Example: - /// ```motoko include=import - /// Int8.toText(-123) // => "-123" - /// ``` - public func toText(x : Int8) : Text { - Int.toText(toInt(x)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 7` (the minimum `Int8` value). - /// - /// Example: - /// ```motoko include=import - /// Int8.abs(-123) // => +123 - /// ``` - public func abs(x : Int8) : Int8 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Int8.min(+2, -3) // => -3 - /// ``` - public func min(x : Int8, y : Int8) : Int8 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Int8.max(+2, -3) // => +2 - /// ``` - public func max(x : Int8, y : Int8) : Int8 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int8 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// Int8.equal(-1, -1); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Buffer "mo:base/Buffer"; - /// - /// let buffer1 = Buffer.Buffer(1); - /// buffer1.add(-3); - /// let buffer2 = Buffer.Buffer(1); - /// buffer2.add(-3); - /// Buffer.equal(buffer1, buffer2, Int8.equal) // => true - /// ``` - public func equal(x : Int8, y : Int8) : Bool { x == y }; - - /// Inequality function for Int8 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// Int8.notEqual(-1, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int8, y : Int8) : Bool { x != y }; - - /// "Less than" function for Int8 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// Int8.less(-2, 1); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int8, y : Int8) : Bool { x < y }; - - /// "Less than or equal" function for Int8 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// Int8.lessOrEqual(-2, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int8, y : Int8) : Bool { x <= y }; - - /// "Greater than" function for Int8 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// Int8.greater(-2, -3); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int8, y : Int8) : Bool { x > y }; - - /// "Greater than or equal" function for Int8 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// Int8.greaterOrEqual(-2, -2); // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int8, y : Int8) : Bool { x >= y }; - - /// General-purpose comparison function for `Int8`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// Int8.compare(-3, 2) // => #less - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.sort([1, -2, -3] : [Int8], Int8.compare) // => [-3, -2, 1] - /// ``` - public func compare(x : Int8, y : Int8) : { #less; #equal; #greater } { - if (x < y) { #less } else if (x == y) { #equal } else { #greater } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 7)`. - /// - /// Example: - /// ```motoko include=import - /// Int8.neg(123) // => -123 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int8) : Int8 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int8.add(100, 23) // => +123 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 0, Int8.add) // => -4 - /// ``` - public func add(x : Int8, y : Int8) : Int8 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int8.sub(123, 23) // => +100 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 0, Int8.sub) // => 4 - /// ``` - public func sub(x : Int8, y : Int8) : Int8 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int8.mul(12, 10) // => +120 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([1, -2, -3], 1, Int8.mul) // => 6 - /// ``` - public func mul(x : Int8, y : Int8) : Int8 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// Int8.div(123, 10) // => +12 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int8, y : Int8) : Int8 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// Int8.rem(123, 10) // => +3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int8, y : Int8) : Int8 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 8`. - /// - /// Example: - /// ```motoko include=import - /// Int8.pow(2, 6) // => +64 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int8, y : Int8) : Int8 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitnot(-16 /* 0xf0 */) // => +15 // 0x0f - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int8) : Int8 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitand(0x1f, 0x70) // => +16 // 0x10 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int8, y : Int8) : Int8 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitor(0x0f, 0x70) // => +127 // 0x7f - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int8, y : Int8) : Int8 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitxor(0x70, 0x7f) // => +15 // 0x0f - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int8, y : Int8) : Int8 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 8`, the semantics is the same as for `bitshiftLeft(x, y % 8)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitshiftLeft(1, 4) // => +16 // 0x10 equivalent to `2 ** 4`. - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int8, y : Int8) : Int8 { x << y }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 8`, the semantics is the same as for `bitshiftRight(x, y % 8)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitshiftRight(64, 4) // => +4 // equivalent to `64 / (2 ** 4)` - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int8, y : Int8) : Int8 { x >> y }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like other bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 8`, the semantics is the same as for `bitrotLeft(x, y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitrotLeft(0x11 /* 0b0001_0001 */, 2) // => +68 // 0b0100_0100 == 0x44. - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int8, y : Int8) : Int8 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like other bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 8`, the semantics is the same as for `bitrotRight(x, y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitrotRight(0x11 /* 0b0001_0001 */, 1) // => -120 // 0b1000_1000 == 0x88. - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int8, y : Int8) : Int8 { x <>> y }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 8`, the semantics is the same as for `bittest(x, p % 8)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// Int8.bittest(64, 6) // => true - /// ``` - public func bittest(x : Int8, p : Nat) : Bool { - Prim.btstInt8(x, Prim.intToInt8(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 8`, the semantics is the same as for `bitset(x, p % 8)`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitset(0, 6) // => +64 - /// ``` - public func bitset(x : Int8, p : Nat) : Int8 { - x | (1 << Prim.intToInt8(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 8`, the semantics is the same as for `bitclear(x, p % 8)`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitclear(-1, 6) // => -65 - /// ``` - public func bitclear(x : Int8, p : Nat) : Int8 { - x & ^(1 << Prim.intToInt8(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 8`, the semantics is the same as for `bitclear(x, p % 8)`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitflip(127, 6) // => +63 - /// ``` - public func bitflip(x : Int8, p : Nat) : Int8 { - x ^ (1 << Prim.intToInt8(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitcountNonZero(0x0f) // => +4 - /// ``` - public let bitcountNonZero : (x : Int8) -> Int8 = Prim.popcntInt8; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitcountLeadingZero(0x08) // => +4 - /// ``` - public let bitcountLeadingZero : (x : Int8) -> Int8 = Prim.clzInt8; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Int8.bitcountTrailingZero(0x10) // => +4 - /// ``` - public let bitcountTrailingZero : (x : Int8) -> Int8 = Prim.ctzInt8; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int8.addWrap(2 ** 6, 2 ** 6) // => -128 // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int8, y : Int8) : Int8 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int8.subWrap(-2 ** 7, 1) // => +127 // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int8, y : Int8) : Int8 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Int8.mulWrap(2 ** 4, 2 ** 4) // => 0 // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int8, y : Int8) : Int8 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 8`. - /// - /// Example: - /// ```motoko include=import - /// Int8.powWrap(2, 7) // => -128 // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int8, y : Int8) : Int8 { x **% y }; - -} diff --git a/.mops/base@0.11.1/src/Iter.mo b/.mops/base@0.11.1/src/Iter.mo deleted file mode 100644 index 3066087..0000000 --- a/.mops/base@0.11.1/src/Iter.mo +++ /dev/null @@ -1,227 +0,0 @@ -/// Iterators - -import Array "Array"; -import Buffer "Buffer"; -import List "List"; -import Order "Order"; - -module { - - /// An iterator that produces values of type `T`. Calling `next` returns - /// `null` when iteration is finished. - /// - /// Iterators are inherently stateful. Calling `next` "consumes" a value from - /// the Iterator that cannot be put back, so keep that in mind when sharing - /// iterators between consumers. - /// - /// An iterater `i` can be iterated over using - /// ``` - /// for (x in i) { - /// …do something with x… - /// } - /// ``` - public type Iter = { next : () -> ?T }; - - /// Creates an iterator that produces all `Nat`s from `x` to `y` including - /// both of the bounds. - /// ```motoko - /// import Iter "mo:base/Iter"; - /// let iter = Iter.range(1, 3); - /// assert(?1 == iter.next()); - /// assert(?2 == iter.next()); - /// assert(?3 == iter.next()); - /// assert(null == iter.next()); - /// ``` - public class range(x : Nat, y : Int) { - var i = x; - public func next() : ?Nat { - if (i > y) { null } else { let j = i; i += 1; ?j } - } - }; - - /// Like `range` but produces the values in the opposite - /// order. - public class revRange(x : Int, y : Int) { - var i = x; - public func next() : ?Int { - if (i < y) { null } else { let j = i; i -= 1; ?j } - } - }; - - /// Calls a function `f` on every value produced by an iterator and discards - /// the results. If you're looking to keep these results use `map` instead. - /// - /// ```motoko - /// import Iter "mo:base/Iter"; - /// var sum = 0; - /// Iter.iterate(Iter.range(1, 3), func(x, _index) { - /// sum += x; - /// }); - /// assert(6 == sum) - /// ``` - public func iterate( - xs : Iter, - f : (A, Nat) -> () - ) { - var i = 0; - label l loop { - switch (xs.next()) { - case (?next) { - f(next, i) - }; - case (null) { - break l - } - }; - i += 1; - continue l - } - }; - - /// Consumes an iterator and counts how many elements were produced - /// (discarding them in the process). - public func size(xs : Iter) : Nat { - var len = 0; - iterate(xs, func(x, i) { len += 1 }); - len - }; - - /// Takes a function and an iterator and returns a new iterator that lazily applies - /// the function to every element produced by the argument iterator. - /// ```motoko - /// import Iter "mo:base/Iter"; - /// let iter = Iter.range(1, 3); - /// let mappedIter = Iter.map(iter, func (x : Nat) : Nat { x * 2 }); - /// assert(?2 == mappedIter.next()); - /// assert(?4 == mappedIter.next()); - /// assert(?6 == mappedIter.next()); - /// assert(null == mappedIter.next()); - /// ``` - public func map(xs : Iter, f : A -> B) : Iter = object { - public func next() : ?B { - switch (xs.next()) { - case (?next) { - ?f(next) - }; - case (null) { - null - } - } - } - }; - - /// Takes a function and an iterator and returns a new iterator that produces - /// elements from the original iterator if and only if the predicate is true. - /// ```motoko - /// import Iter "o:base/Iter"; - /// let iter = Iter.range(1, 3); - /// let mappedIter = Iter.filter(iter, func (x : Nat) : Bool { x % 2 == 1 }); - /// assert(?1 == mappedIter.next()); - /// assert(?3 == mappedIter.next()); - /// assert(null == mappedIter.next()); - /// ``` - public func filter(xs : Iter, f : A -> Bool) : Iter = object { - public func next() : ?A { - loop { - switch (xs.next()) { - case (null) { - return null - }; - case (?x) { - if (f(x)) { - return ?x - } - } - } - }; - null - } - }; - - /// Creates an iterator that produces an infinite sequence of `x`. - /// ```motoko - /// import Iter "mo:base/Iter"; - /// let iter = Iter.make(10); - /// assert(?10 == iter.next()); - /// assert(?10 == iter.next()); - /// assert(?10 == iter.next()); - /// // ... - /// ``` - public func make(x : A) : Iter = object { - public func next() : ?A { - ?x - } - }; - - /// Creates an iterator that produces the elements of an Array in ascending index order. - /// ```motoko - /// import Iter "mo:base/Iter"; - /// let iter = Iter.fromArray([1, 2, 3]); - /// assert(?1 == iter.next()); - /// assert(?2 == iter.next()); - /// assert(?3 == iter.next()); - /// assert(null == iter.next()); - /// ``` - public func fromArray(xs : [A]) : Iter { - var ix : Nat = 0; - let size = xs.size(); - object { - public func next() : ?A { - if (ix >= size) { - return null - } else { - let res = ?(xs[ix]); - ix += 1; - return res - } - } - } - }; - - /// Like `fromArray` but for Arrays with mutable elements. Captures - /// the elements of the Array at the time the iterator is created, so - /// further modifications won't be reflected in the iterator. - public func fromArrayMut(xs : [var A]) : Iter { - fromArray(Array.freeze(xs)) - }; - - /// Like `fromArray` but for Lists. - public let fromList = List.toIter; - - /// Consumes an iterator and collects its produced elements in an Array. - /// ```motoko - /// import Iter "mo:base/Iter"; - /// let iter = Iter.range(1, 3); - /// assert([1, 2, 3] == Iter.toArray(iter)); - /// ``` - public func toArray(xs : Iter) : [A] { - let buffer = Buffer.Buffer(8); - iterate(xs, func(x : A, _ix : Nat) { buffer.add(x) }); - return Buffer.toArray(buffer) - }; - - /// Like `toArray` but for Arrays with mutable elements. - public func toArrayMut(xs : Iter) : [var A] { - Array.thaw(toArray(xs)) - }; - - /// Like `toArray` but for Lists. - public func toList(xs : Iter) : List.List { - var result = List.nil(); - iterate( - xs, - func(x, _i) { - result := List.push(x, result) - } - ); - List.reverse(result) - }; - - /// Sorted iterator. Will iterate over *all* elements to sort them, necessarily. - public func sort(xs : Iter, compare : (A, A) -> Order.Order) : Iter { - let a = toArrayMut(xs); - Array.sortInPlace(a, compare); - fromArrayMut(a) - }; - -} diff --git a/.mops/base@0.11.1/src/IterType.mo b/.mops/base@0.11.1/src/IterType.mo deleted file mode 100644 index 1c258ff..0000000 --- a/.mops/base@0.11.1/src/IterType.mo +++ /dev/null @@ -1,7 +0,0 @@ -/// The Iterator type - -// Just here to break cyclic module definitions - -module { - public type Iter = { next : () -> ?T } -} diff --git a/.mops/base@0.11.1/src/List.mo b/.mops/base@0.11.1/src/List.mo deleted file mode 100644 index 10369a8..0000000 --- a/.mops/base@0.11.1/src/List.mo +++ /dev/null @@ -1,932 +0,0 @@ -/// Purely-functional, singly-linked lists. - -/// A list of type `List` is either `null` or an optional pair of a value of type `T` and a tail, itself of type `List`. -/// -/// To use this library, import it using: -/// -/// ```motoko name=initialize -/// import List "mo:base/List"; -/// ``` - -import Array "Array"; -import Iter "IterType"; -import Option "Option"; -import Order "Order"; -import Result "Result"; - -module { - - // A singly-linked list consists of zero or more _cons cells_, wherein - // each cell contains a single list element (the cell's _head_), and a pointer to the - // remainder of the list (the cell's _tail_). - public type List = ?(T, List); - - /// Create an empty list. - /// - /// Example: - /// ```motoko include=initialize - /// List.nil() // => null - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func nil() : List = null; - - /// Check whether a list is empty and return true if the list is empty. - /// - /// Example: - /// ```motoko include=initialize - /// List.isNil(null) // => true - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func isNil(l : List) : Bool { - switch l { - case null { true }; - case _ { false } - } - }; - - /// Add `x` to the head of `list`, and return the new list. - /// - /// Example: - /// ```motoko include=initialize - /// List.push(0, null) // => ?(0, null); - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func push(x : T, l : List) : List = ?(x, l); - - /// Return the last element of the list, if present. - /// Example: - /// ```motoko include=initialize - /// List.last(?(0, ?(1, null))) // => ?1 - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func last(l : List) : ?T { - switch l { - case null { null }; - case (?(x, null)) { ?x }; - case (?(_, t)) { last(t) } - } - }; - - /// Remove the head of the list, returning the optioned head and the tail of the list in a pair. - /// Returns `(null, null)` if the list is empty. - /// - /// Example: - /// ```motoko include=initialize - /// List.pop(?(0, ?(1, null))) // => (?0, ?(1, null)) - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func pop(l : List) : (?T, List) { - switch l { - case null { (null, null) }; - case (?(h, t)) { (?h, t) } - } - }; - - /// Return the length of the list. - /// - /// Example: - /// ```motoko include=initialize - /// List.size(?(0, ?(1, null))) // => 2 - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func size(l : List) : Nat { - func rec(l : List, n : Nat) : Nat { - switch l { - case null { n }; - case (?(_, t)) { rec(t, n + 1) } - } - }; - rec(l, 0) - }; - /// Access any item in a list, zero-based. - /// - /// NOTE: Indexing into a list is a linear operation, and usually an - /// indication that a list might not be the best data structure - /// to use. - /// - /// Example: - /// ```motoko include=initialize - /// List.get(?(0, ?(1, null)), 1) // => ?1 - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func get(l : List, n : Nat) : ?T { - switch (n, l) { - case (_, null) { null }; - case (0, (?(h, t))) { ?h }; - case (_, (?(_, t))) { get(t, n - 1) } - } - }; - - /// Reverses the list. - /// - /// Example: - /// ```motoko include=initialize - /// List.reverse(?(0, ?(1, ?(2, null)))) // => ?(2, ?(1, ?(0, null))) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func reverse(l : List) : List { - func rec(l : List, r : List) : List { - switch l { - case null { r }; - case (?(h, t)) { rec(t, ?(h, r)) } - } - }; - rec(l, null) - }; - - /// Call the given function for its side effect, with each list element in turn. - /// - /// Example: - /// ```motoko include=initialize - /// var sum = 0; - /// List.iterate(?(0, ?(1, ?(2, null))), func n { sum += n }); - /// sum // => 3 - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func iterate(l : List, f : T -> ()) { - switch l { - case null { () }; - case (?(h, t)) { f(h); iterate(t, f) } - } - }; - - /// Call the given function `f` on each list element and collect the results - /// in a new list. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat = "mo:base/Nat" - /// List.map(?(0, ?(1, ?(2, null))), Nat.toText) // => ?("0", ?("1", ?("2", null)) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func map(l : List, f : T -> U) : List { - switch l { - case null { null }; - case (?(h, t)) { ?(f(h), map(t, f)) } - } - }; - - /// Create a new list with only those elements of the original list for which - /// the given function (often called the _predicate_) returns true. - /// - /// Example: - /// ```motoko include=initialize - /// List.filter(?(0, ?(1, ?(2, null))), func n { n != 1 }) // => ?(0, ?(2, null)) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func filter(l : List, f : T -> Bool) : List { - switch l { - case null { null }; - case (?(h, t)) { - if (f(h)) { - ?(h, filter(t, f)) - } else { - filter(t, f) - } - } - } - }; - - /// Create two new lists from the results of a given function (`f`). - /// The first list only includes the elements for which the given - /// function `f` returns true and the second list only includes - /// the elements for which the function returns false. - /// - /// Example: - /// ```motoko include=initialize - /// List.partition(?(0, ?(1, ?(2, null))), func n { n != 1 }) // => (?(0, ?(2, null)), ?(1, null)) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func partition(l : List, f : T -> Bool) : (List, List) { - switch l { - case null { (null, null) }; - case (?(h, t)) { - if (f(h)) { - // call f in-order - let (l, r) = partition(t, f); - (?(h, l), r) - } else { - let (l, r) = partition(t, f); - (l, ?(h, r)) - } - } - } - }; - - /// Call the given function on each list element, and collect the non-null results - /// in a new list. - /// - /// Example: - /// ```motoko include=initialize - /// List.mapFilter( - /// ?(1, ?(2, ?(3, null))), - /// func n { - /// if (n > 1) { - /// ?(n * 2); - /// } else { - /// null - /// } - /// } - /// ) // => ?(4, ?(6, null)) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapFilter(l : List, f : T -> ?U) : List { - switch l { - case null { null }; - case (?(h, t)) { - switch (f(h)) { - case null { mapFilter(t, f) }; - case (?h_) { ?(h_, mapFilter(t, f)) } - } - } - } - }; - - /// Maps a Result-returning function `f` over a List and returns either - /// the first error or a list of successful values. - /// - /// Example: - /// ```motoko include=initialize - /// List.mapResult( - /// ?(1, ?(2, ?(3, null))), - /// func n { - /// if (n > 0) { - /// #ok(n * 2); - /// } else { - /// #err("Some element is zero") - /// } - /// } - /// ); // => #ok ?(2, ?(4, ?(6, null)) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapResult(xs : List, f : T -> Result.Result) : Result.Result, E> { - func go(xs : List, acc : List) : Result.Result, E> { - switch xs { - case null { #ok(acc) }; - case (?(head, tail)) { - switch (f(head)) { - case (#err(err)) { #err(err) }; - case (#ok(ok)) { go(tail, ?(ok, acc)) } - } - } - } - }; - Result.mapOk(go(xs, null), func(xs : List) : List = reverse(xs)) - }; - - /// Append the elements from the reverse of one list, 'l', to another list, 'm'. - /// - /// Example: - /// ```motoko include=initialize - /// List.revAppend( - /// ?(2, ?(1, ?(0, null))), - /// ?(3, ?(4, ?(5, null))) - /// ); // => ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))) - /// ``` - /// - /// Runtime: O(size(l)) - /// - /// Space: O(size(l)) - func revAppend(l : List, m : List) : List { - switch l { - case null { m }; - case (?(h, t)) { revAppend(t, ?(h, m)) } - } - }; - - /// Append the elements from one list to another list. - /// - /// Example: - /// ```motoko include=initialize - /// List.append( - /// ?(0, ?(1, ?(2, null))), - /// ?(3, ?(4, ?(5, null))) - /// ) // => ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))) - /// ``` - /// - /// Runtime: O(size(l)) - /// - /// Space: O(size(l)) - public func append(l : List, m : List) : List { - revAppend(reverse(l), m) - }; - - /// Flatten, or concatenate, a list of lists as a list. - /// - /// Example: - /// ```motoko include=initialize - /// List.flatten( - /// ?(?(0, ?(1, ?(2, null))), - /// ?(?(3, ?(4, ?(5, null))), - /// null)) - /// ); // => ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))) - /// ``` - /// - /// Runtime: O(size*size) - /// - /// Space: O(size*size) - public func flatten(l : List>) : List { - //FIXME: this is quadratic, not linear https://github.com/dfinity/motoko-base/issues/459 - foldLeft, List>(l, null, func(a, b) { append(a, b) }) - }; - - /// Returns the first `n` elements of the given list. - /// If the given list has fewer than `n` elements, this function returns - /// a copy of the full input list. - /// - /// Example: - /// ```motoko include=initialize - /// List.take( - /// ?(0, ?(1, ?(2, null))), - /// 2 - /// ); // => ?(0, ?(1, null)) - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - public func take(l : List, n : Nat) : List { - switch (l, n) { - case (_, 0) { null }; - case (null, _) { null }; - case (?(h, t), m) { ?(h, take(t, m - 1)) } - } - }; - - /// Drop the first `n` elements from the given list. - /// - /// Example: - /// ```motoko include=initialize - /// List.drop( - /// ?(0, ?(1, ?(2, null))), - /// 2 - /// ); // => ?(2, null) - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(1) - public func drop(l : List, n : Nat) : List { - switch (l, n) { - case (l_, 0) { l_ }; - case (null, _) { null }; - case ((?(h, t)), m) { drop(t, m - 1) } - } - }; - - /// Collapses the elements in `list` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// List.foldLeft( - /// ?(1, ?(2, ?(3, null))), - /// "", - /// func (acc, x) { acc # Nat.toText(x)} - /// ) // => "123" - /// ``` - /// - /// Runtime: O(size(list)) - /// - /// Space: O(1) heap, O(1) stack - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(list : List, base : S, combine : (S, T) -> S) : S { - switch list { - case null { base }; - case (?(h, t)) { foldLeft(t, combine(base, h), combine) } - } - }; - - /// Collapses the elements in `buffer` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// List.foldRight( - /// ?(1, ?(2, ?(3, null))), - /// "", - /// func (x, acc) { Nat.toText(x) # acc} - /// ) // => "123" - /// ``` - /// - /// Runtime: O(size(list)) - /// - /// Space: O(1) heap, O(size(list)) stack - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldRight(list : List, base : S, combine : (T, S) -> S) : S { - switch list { - case null { base }; - case (?(h, t)) { combine(h, foldRight(t, base, combine)) } - } - }; - - /// Return the first element for which the given predicate `f` is true, - /// if such an element exists. - /// - /// Example: - /// ```motoko include=initialize - /// - /// List.find( - /// ?(1, ?(2, ?(3, null))), - /// func n { n > 1 } - /// ); // => ?2 - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func find(l : List, f : T -> Bool) : ?T { - switch l { - case null { null }; - case (?(h, t)) { if (f(h)) { ?h } else { find(t, f) } } - } - }; - - /// Return true if there exists a list element for which - /// the given predicate `f` is true. - /// - /// Example: - /// ```motoko include=initialize - /// - /// List.some( - /// ?(1, ?(2, ?(3, null))), - /// func n { n > 1 } - /// ) // => true - /// ``` - /// - /// Runtime: O(size(list)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func some(l : List, f : T -> Bool) : Bool { - switch l { - case null { false }; - case (?(h, t)) { f(h) or some(t, f) } - } - }; - - /// Return true if the given predicate `f` is true for all list - /// elements. - /// - /// Example: - /// ```motoko include=initialize - /// - /// List.all( - /// ?(1, ?(2, ?(3, null))), - /// func n { n > 1 } - /// ); // => false - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func all(l : List, f : T -> Bool) : Bool { - switch l { - case null { true }; - case (?(h, t)) { f(h) and all(t, f) } - } - }; - - /// Merge two ordered lists into a single ordered list. - /// This function requires both list to be ordered as specified - /// by the given relation `lessThanOrEqual`. - /// - /// Example: - /// ```motoko include=initialize - /// - /// List.merge( - /// ?(1, ?(2, ?(4, null))), - /// ?(2, ?(4, ?(6, null))), - /// func (n1, n2) { n1 <= n2 } - /// ); // => ?(1, ?(2, ?(2, ?(4, ?(4, ?(6, null))))))), - /// ``` - /// - /// Runtime: O(size(l1) + size(l2)) - /// - /// Space: O(size(l1) + size(l2)) - /// - /// *Runtime and space assumes that `lessThanOrEqual` runs in O(1) time and space. - // TODO: replace by merge taking a compare : (T, T) -> Order.Order function? - public func merge(l1 : List, l2 : List, lessThanOrEqual : (T, T) -> Bool) : List { - switch (l1, l2) { - case (null, _) { l2 }; - case (_, null) { l1 }; - case (?(h1, t1), ?(h2, t2)) { - if (lessThanOrEqual(h1, h2)) { - ?(h1, merge(t1, l2, lessThanOrEqual)) - } else { - ?(h2, merge(l1, t2, lessThanOrEqual)) - } - } - } - }; - - private func compareAux(l1 : List, l2 : List, compare : (T, T) -> Order.Order) : Order.Order { - switch (l1, l2) { - case (null, null) { #equal }; - case (null, _) { #less }; - case (_, null) { #greater }; - case (?(h1, t1), ?(h2, t2)) { - switch (compare(h1, h2)) { - case (#equal) { compareAux(t1, t2, compare) }; - case other { other } - } - } - } - }; - - /// Compare two lists using lexicographic ordering specified by argument function `compare`. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// List.compare( - /// ?(1, ?(2, null)), - /// ?(3, ?(4, null)), - /// Nat.compare - /// ) // => #less - /// ``` - /// - /// Runtime: O(size(l1)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that argument `compare` runs in O(1) time and space. - public func compare(l1 : List, l2 : List, compare : (T, T) -> Order.Order) : Order.Order { - compareAux(l1, l2, compare); - }; - - private func equalAux(l1 : List, l2 : List, equal : (T, T) -> Bool) : Bool { - switch (l1, l2) { - case (?(h1, t1), ?(h2, t2)) { - equal(h1, h2) and equalAux(t1, t2, equal) - }; - case (null, null) { true }; - case _ { false }; - } - }; - /// Compare two lists for equality using the argument function `equal` to determine equality of their elements. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat "mo:base/Nat"; - /// - /// List.equal( - /// ?(1, ?(2, null)), - /// ?(3, ?(4, null)), - /// Nat.equal - /// ); // => false - /// ``` - /// - /// Runtime: O(size(l1)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that argument `equal` runs in O(1) time and space. - public func equal(l1 : List, l2 : List, equal : (T, T) -> Bool) : Bool { - equalAux(l1, l2, equal); - }; - - /// Generate a list based on a length and a function that maps from - /// a list index to a list element. - /// - /// Example: - /// ```motoko include=initialize - /// List.tabulate( - /// 3, - /// func n { n * 2 } - /// ) // => ?(0, ?(2, (?4, null))) - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func tabulate(n : Nat, f : Nat -> T) : List { - var i = 0; - var l : List = null; - while (i < n) { - l := ?(f(i), l); - i += 1 - }; - reverse(l) - }; - - /// Create a list with exactly one element. - /// - /// Example: - /// ```motoko include=initialize - /// List.make( - /// 0 - /// ) // => ?(0, null) - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func make(x : T) : List = ?(x, null); - - /// Create a list of the given length with the same value in each position. - /// - /// Example: - /// ```motoko include=initialize - /// List.replicate( - /// 3, - /// 0 - /// ) // => ?(0, ?(0, ?(0, null))) - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - public func replicate(n : Nat, x : T) : List { - var i = 0; - var l : List = null; - while (i < n) { - l := ?(x, l); - i += 1 - }; - l - }; - - /// Create a list of pairs from a pair of lists. - /// - /// If the given lists have different lengths, then the created list will have a - /// length equal to the length of the smaller list. - /// - /// Example: - /// ```motoko include=initialize - /// List.zip( - /// ?(0, ?(1, ?(2, null))), - /// ?("0", ?("1", null)), - /// ) // => ?((0, "0"), ?((1, "1"), null)) - /// ``` - /// - /// Runtime: O(min(size(xs), size(ys))) - /// - /// Space: O(min(size(xs), size(ys))) - public func zip(xs : List, ys : List) : List<(T, U)> = zipWith(xs, ys, func(x, y) { (x, y) }); - - /// Create a list in which elements are created by applying function `f` to each pair `(x, y)` of elements - /// occuring at the same position in list `xs` and list `ys`. - /// - /// If the given lists have different lengths, then the created list will have a - /// length equal to the length of the smaller list. - /// - /// Example: - /// ```motoko include=initialize - /// import Nat = "mo:base/Nat"; - /// import Char = "mo:base/Char"; - /// - /// List.zipWith( - /// ?(0, ?(1, ?(2, null))), - /// ?('a', ?('b', null)), - /// func (n, c) { Nat.toText(n) # Char.toText(c) } - /// ) // => ?("0a", ?("1b", null)) - /// ``` - /// - /// Runtime: O(min(size(xs), size(ys))) - /// - /// Space: O(min(size(xs), size(ys))) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func zipWith( - xs : List, - ys : List, - f : (T, U) -> V - ) : List { - switch (pop(xs)) { - case (null, _) { null }; - case (?x, xt) { - switch (pop(ys)) { - case (null, _) { null }; - case (?y, yt) { - push(f(x, y), zipWith(xt, yt, f)) - } - } - } - } - }; - - /// Split the given list at the given zero-based index. - /// - /// Example: - /// ```motoko include=initialize - /// List.split( - /// 2, - /// ?(0, ?(1, ?(2, null))) - /// ) // => (?(0, ?(1, null)), ?(2, null)) - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - public func split(n : Nat, xs : List) : (List, List) { - if (n == 0) { (null, xs) } else { - func rec(n : Nat, xs : List) : (List, List) { - switch (pop(xs)) { - case (null, _) { (null, null) }; - case (?h, t) { - if (n == 1) { (make(h), t) } else { - let (l, r) = rec(n - 1, t); - (push(h, l), r) - } - } - } - }; - rec(n, xs) - } - }; - - /// Split the given list into chunks of length `n`. - /// The last chunk will be shorter if the length of the given list - /// does not divide by `n` evenly. - /// - /// Example: - /// ```motoko include=initialize - /// List.chunks( - /// 2, - /// ?(0, ?(1, ?(2, ?(3, ?(4, null))))) - /// ) - /// /* => ?(?(0, ?(1, null)), - /// ?(?(2, ?(3, null)), - /// ?(?(4, null), - /// null))) - /// */ - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func chunks(n : Nat, xs : List) : List> { - let (l, r) = split(n, xs); - if (isNil(l)) { - null - } else { - push>(l, chunks(n, r)) - } - }; - - /// Convert an array into a list. - /// - /// Example: - /// ```motoko include=initialize - /// List.fromArray([ 0, 1, 2, 3, 4]) - /// // => ?(0, ?(1, ?(2, ?(3, ?(4, null))))) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromArray(xs : [T]) : List { - Array.foldRight>( - xs, - null, - func(x : T, ys : List) : List { - push(x, ys) - } - ) - }; - - /// Convert a mutable array into a list. - /// - /// Example: - /// ```motoko include=initialize - /// List.fromVarArray([var 0, 1, 2, 3, 4]) - /// // => ?(0, ?(1, ?(2, ?(3, ?(4, null))))) - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromVarArray(xs : [var T]) : List = fromArray(Array.freeze(xs)); - - /// Create an array from a list. - /// Example: - /// ```motoko include=initialize - /// List.toArray(?(0, ?(1, ?(2, ?(3, ?(4, null)))))) - /// // => [0, 1, 2, 3, 4] - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toArray(xs : List) : [T] { - let length = size(xs); - var list = xs; - Array.tabulate( - length, - func(i) { - let popped = pop(list); - list := popped.1; - switch (popped.0) { - case null { loop { assert false } }; - case (?x) x - } - } - ) - }; - - /// Create a mutable array from a list. - /// Example: - /// ```motoko include=initialize - /// List.toVarArray(?(0, ?(1, ?(2, ?(3, ?(4, null)))))) - /// // => [var 0, 1, 2, 3, 4] - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toVarArray(xs : List) : [var T] = Array.thaw(toArray(xs)); - - /// Create an iterator from a list. - /// Example: - /// ```motoko include=initialize - /// var sum = 0; - /// for (n in List.toIter(?(0, ?(1, ?(2, ?(3, ?(4, null))))))) { - /// sum += n; - /// }; - /// sum - /// // => 10 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func toIter(xs : List) : Iter.Iter { - var state = xs; - object { - public func next() : ?T = switch state { - case (?(hd, tl)) { state := tl; ?hd }; - case _ null - } - } - } - -} diff --git a/.mops/base@0.11.1/src/Nat.mo b/.mops/base@0.11.1/src/Nat.mo deleted file mode 100644 index caa3f90..0000000 --- a/.mops/base@0.11.1/src/Nat.mo +++ /dev/null @@ -1,335 +0,0 @@ -/// Natural numbers with infinite precision. -/// -/// Most operations on natural numbers (e.g. addition) are available as built-in operators (e.g. `1 + 1`). -/// This module provides equivalent functions and `Text` conversion. -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Nat "mo:base/Nat"; -/// ``` - -import Int "Int"; -import Order "Order"; -import Prim "mo:⛔"; -import Char "Char"; - -module { - - /// Infinite precision natural numbers. - public type Nat = Prim.Types.Nat; - - /// Converts a natural number to its textual representation. Textual - /// representation _do not_ contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// Nat.toText 1234 // => "1234" - /// ``` - public func toText(n : Nat) : Text = Int.toText n; - - /// Creates a natural number from its textual representation. Returns `null` - /// if the input is not a valid natural number. - /// - /// Note: The textual representation _must not_ contain underscores. - /// - /// Example: - /// ```motoko include=import - /// Nat.fromText "1234" // => ?1234 - /// ``` - public func fromText(text : Text) : ?Nat { - if (text == "") { - return null - }; - var n = 0; - for (c in text.chars()) { - if (Char.isDigit(c)) { - let charAsNat = Prim.nat32ToNat(Prim.charToNat32(c) -% Prim.charToNat32('0')); - n := n * 10 + charAsNat - } else { - return null - } - }; - ?n - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat.min(1, 2) // => 1 - /// ``` - public func min(x : Nat, y : Nat) : Nat { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat.max(1, 2) // => 2 - /// ``` - public func max(x : Nat, y : Nat) : Nat { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat.equal(1, 1); // => true - /// 1 == 1 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Buffer "mo:base/Buffer"; - /// - /// let buffer1 = Buffer.Buffer(3); - /// let buffer2 = Buffer.Buffer(3); - /// Buffer.equal(buffer1, buffer2, Nat.equal) // => true - /// ``` - public func equal(x : Nat, y : Nat) : Bool { x == y }; - - /// Inequality function for Nat types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat.notEqual(1, 2); // => true - /// 1 != 2 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat, y : Nat) : Bool { x != y }; - - /// "Less than" function for Nat types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat.less(1, 2); // => true - /// 1 < 2 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat, y : Nat) : Bool { x < y }; - - /// "Less than or equal" function for Nat types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat.lessOrEqual(1, 2); // => true - /// 1 <= 2 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat, y : Nat) : Bool { x <= y }; - - /// "Greater than" function for Nat types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat.greater(2, 1); // => true - /// 2 > 1 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat, y : Nat) : Bool { x > y }; - - /// "Greater than or equal" function for Nat types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat.greaterOrEqual(2, 1); // => true - /// 2 >= 1 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat, y : Nat) : Bool { x >= y }; - - /// General purpose comparison function for `Nat`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat.compare(2, 3) // => #less - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.sort([2, 3, 1], Nat.compare) // => [1, 2, 3] - /// ``` - public func compare(x : Nat, y : Nat) : { #less; #equal; #greater } { - if (x < y) { #less } else if (x == y) { #equal } else { #greater } - }; - - /// Returns the sum of `x` and `y`, `x + y`. This operator will never overflow - /// because `Nat` is infinite precision. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat.add(1, 2); // => 3 - /// 1 + 2 // => 3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 0, Nat.add) // => 6 - /// ``` - public func add(x : Nat, y : Nat) : Nat { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow below `0`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat.sub(2, 1); // => 1 - /// // Add a type annotation to avoid a warning about the subtraction - /// 2 - 1 : Nat // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 10, Nat.sub) // => 4 - /// ``` - public func sub(x : Nat, y : Nat) : Nat { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. This operator will never - /// overflow because `Nat` is infinite precision. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat.mul(2, 3); // => 6 - /// 2 * 3 // => 6 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 1, Nat.mul) // => 6 - /// ``` - public func mul(x : Nat, y : Nat) : Nat { x * y }; - - /// Returns the unsigned integer division of `x` by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// The quotient is rounded down, which is equivalent to truncating the - /// decimal places of the quotient. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat.div(6, 2); // => 3 - /// 6 / 2 // => 3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat, y : Nat) : Nat { x / y }; - - /// Returns the remainder of unsigned integer division of `x` by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat.rem(6, 4); // => 2 - /// 6 % 4 // => 2 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat, y : Nat) : Nat { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. Traps when `y > 2^32`. This operator - /// will never overflow because `Nat` is infinite precision. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat.pow(2, 3); // => 8 - /// 2 ** 3 // => 8 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat, y : Nat) : Nat { x ** y }; - - /// Returns the (conceptual) bitwise shift left of `x` by `y`, `x * (2 ** y)`. - /// - /// Example: - /// ```motoko include=import - /// Nat.bitshiftLeft(1, 3); // => 8 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in absence - /// of the `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. While `Nat` is not defined in terms - /// of bit patterns, conceptually it can be regarded as such, and the operation - /// is provided as a high-performance version of the corresponding arithmetic - /// rule. - public func bitshiftLeft(x : Nat, y : Nat32) : Nat { Prim.shiftLeft(x, y) }; - - /// Returns the (conceptual) bitwise shift right of `x` by `y`, `x / (2 ** y)`. - /// - /// Example: - /// ```motoko include=import - /// Nat.bitshiftRight(8, 3); // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in absence - /// of the `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. While `Nat` is not defined in terms - /// of bit patterns, conceptually it can be regarded as such, and the operation - /// is provided as a high-performance version of the corresponding arithmetic - /// rule. - public func bitshiftRight(x : Nat, y : Nat32) : Nat { Prim.shiftRight(x, y) }; - -} diff --git a/.mops/base@0.11.1/src/Nat16.mo b/.mops/base@0.11.1/src/Nat16.mo deleted file mode 100644 index f8ca1a2..0000000 --- a/.mops/base@0.11.1/src/Nat16.mo +++ /dev/null @@ -1,577 +0,0 @@ -/// Provides utility functions on 16-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Nat16 "mo:base/Nat16"; -/// ``` -import Nat "Nat"; -import Prim "mo:⛔"; - -module { - - /// 16-bit natural numbers. - public type Nat16 = Prim.Types.Nat16; - - /// Maximum 16-bit natural number. `2 ** 16 - 1`. - /// - /// Example: - /// ```motoko include=import - /// Nat16.maximumValue; // => 65536 : Nat16 - /// ``` - public let maximumValue = 65535 : Nat16; - - /// Converts a 16-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// Nat16.toNat(123); // => 123 : Nat - /// ``` - public let toNat : Nat16 -> Nat = Prim.nat16ToNat; - - /// Converts an unsigned integer with infinite precision to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// Nat16.fromNat(123); // => 123 : Nat16 - /// ``` - public let fromNat : Nat -> Nat16 = Prim.natToNat16; - - /// Converts an 8-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// Nat16.fromNat8(123); // => 123 : Nat16 - /// ``` - public func fromNat8(x : Nat8) : Nat16 { - Prim.nat8ToNat16(x) - }; - - /// Converts a 16-bit unsigned integer to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// Nat16.toNat8(123); // => 123 : Nat8 - /// ``` - public func toNat8(x : Nat16) : Nat8 { - Prim.nat16ToNat8(x) - }; - - /// Converts a 32-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// Nat16.fromNat32(123); // => 123 : Nat16 - /// ``` - public func fromNat32(x : Nat32) : Nat16 { - Prim.nat32ToNat16(x) - }; - - /// Converts a 16-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// Nat16.toNat32(123); // => 123 : Nat32 - /// ``` - public func toNat32(x : Nat16) : Nat32 { - Prim.nat16ToNat32(x) - }; - - /// Converts a signed integer with infinite precision to a 16-bit unsigned integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Nat16.fromIntWrap(123 : Int); // => 123 : Nat16 - /// ``` - public let fromIntWrap : Int -> Nat16 = Prim.intToNat16Wrap; - - /// Converts `x` to its textual representation. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// Nat16.toText(1234); // => "1234" : Text - /// ``` - public func toText(x : Nat16) : Text { - Nat.toText(toNat(x)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat16.min(123, 200); // => 123 : Nat16 - /// ``` - public func min(x : Nat16, y : Nat16) : Nat16 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat16.max(123, 200); // => 200 : Nat16 - /// ``` - public func max(x : Nat16, y : Nat16) : Nat16 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat16 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.equal(1, 1); // => true - /// (1 : Nat16) == (1 : Nat16) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Buffer "mo:base/Buffer"; - /// - /// let buffer1 = Buffer.Buffer(3); - /// let buffer2 = Buffer.Buffer(3); - /// Buffer.equal(buffer1, buffer2, Nat16.equal) // => true - /// ``` - public func equal(x : Nat16, y : Nat16) : Bool { x == y }; - - /// Inequality function for Nat16 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.notEqual(1, 2); // => true - /// (1 : Nat16) != (2 : Nat16) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat16, y : Nat16) : Bool { x != y }; - - /// "Less than" function for Nat16 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.less(1, 2); // => true - /// (1 : Nat16) < (2 : Nat16) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat16, y : Nat16) : Bool { x < y }; - - /// "Less than or equal" function for Nat16 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.lessOrEqual(1, 2); // => true - /// (1 : Nat16) <= (2 : Nat16) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat16, y : Nat16) : Bool { x <= y }; - - /// "Greater than" function for Nat16 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.greater(2, 1); // => true - /// (2 : Nat16) > (1 : Nat16) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat16, y : Nat16) : Bool { x > y }; - - /// "Greater than or equal" function for Nat16 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.greaterOrEqual(2, 1); // => true - /// (2 : Nat16) >= (1 : Nat16) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat16, y : Nat16) : Bool { x >= y }; - - /// General purpose comparison function for `Nat16`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat16.compare(2, 3) // => #less - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.sort([2, 3, 1] : [Nat16], Nat16.compare) // => [1, 2, 3] - /// ``` - public func compare(x : Nat16, y : Nat16) : { #less; #equal; #greater } { - if (x < y) { #less } else if (x == y) { #equal } else { #greater } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.add(1, 2); // => 3 - /// (1 : Nat16) + (2 : Nat16) // => 3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 0, Nat16.add) // => 6 - /// ``` - public func add(x : Nat16, y : Nat16) : Nat16 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.sub(2, 1); // => 1 - /// (2 : Nat16) - (1 : Nat16) // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 20, Nat16.sub) // => 14 - /// ``` - public func sub(x : Nat16, y : Nat16) : Nat16 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.mul(2, 3); // => 6 - /// (2 : Nat16) * (3 : Nat16) // => 6 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 1, Nat16.mul) // => 6 - /// ``` - public func mul(x : Nat16, y : Nat16) : Nat16 { x * y }; - - /// Returns the quotient of `x` divided by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.div(6, 2); // => 3 - /// (6 : Nat16) / (2 : Nat16) // => 3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat16, y : Nat16) : Nat16 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.rem(6, 4); // => 2 - /// (6 : Nat16) % (4 : Nat16) // => 2 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat16, y : Nat16) : Nat16 { x % y }; - - /// Returns the power of `x` to `y`, `x ** y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.pow(2, 3); // => 8 - /// (2 : Nat16) ** (3 : Nat16) // => 8 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat16, y : Nat16) : Nat16 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.bitnot(0); // => 65535 - /// ^(0 : Nat16) // => 65535 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat16) : Nat16 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.bitand(0, 1); // => 0 - /// (0 : Nat16) & (1 : Nat16) // => 0 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat16, y : Nat16) : Nat16 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.bitor(0, 1); // => 1 - /// (0 : Nat16) | (1 : Nat16) // => 1 - /// ``` - public func bitor(x : Nat16, y : Nat16) : Nat16 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.bitxor(0, 1); // => 1 - /// (0 : Nat16) ^ (1 : Nat16) // => 1 - /// ``` - public func bitxor(x : Nat16, y : Nat16) : Nat16 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.bitshiftLeft(1, 3); // => 8 - /// (1 : Nat16) << (3 : Nat16) // => 8 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat16, y : Nat16) : Nat16 { x << y }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.bitshiftRight(8, 3); // => 1 - /// (8 : Nat16) >> (3 : Nat16) // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat16, y : Nat16) : Nat16 { x >> y }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.bitrotLeft(2, 1); // => 4 - /// (2 : Nat16) <<> (1 : Nat16) // => 4 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat16, y : Nat16) : Nat16 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.bitrotRight(1, 1); // => 32768 - /// (1 : Nat16) <>> (1 : Nat16) // => 32768 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat16, y : Nat16) : Nat16 { x <>> y }; - - /// Returns the value of bit `p mod 16` in `x`, `(x & 2^(p mod 16)) == 2^(p mod 16)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// Nat16.bittest(5, 2); // => true - /// ``` - public func bittest(x : Nat16, p : Nat) : Bool { - Prim.btstNat16(x, Prim.natToNat16(p)) - }; - - /// Returns the value of setting bit `p mod 16` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// Nat16.bitset(0, 2); // => 4 - /// ``` - public func bitset(x : Nat16, p : Nat) : Nat16 { - x | (1 << Prim.natToNat16(p)) - }; - - /// Returns the value of clearing bit `p mod 16` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// Nat16.bitclear(5, 2); // => 1 - /// ``` - public func bitclear(x : Nat16, p : Nat) : Nat16 { - x & ^(1 << Prim.natToNat16(p)) - }; - - /// Returns the value of flipping bit `p mod 16` in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat16.bitflip(5, 2); // => 1 - /// ``` - public func bitflip(x : Nat16, p : Nat) : Nat16 { - x ^ (1 << Prim.natToNat16(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat16.bitcountNonZero(5); // => 2 - /// ``` - public let bitcountNonZero : (x : Nat16) -> Nat16 = Prim.popcntNat16; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat16.bitcountLeadingZero(5); // => 13 - /// ``` - public let bitcountLeadingZero : (x : Nat16) -> Nat16 = Prim.clzNat16; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat16.bitcountTrailingZero(5); // => 0 - /// ``` - public let bitcountTrailingZero : (x : Nat16) -> Nat16 = Prim.ctzNat16; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.addWrap(65532, 5); // => 1 - /// (65532 : Nat16) +% (5 : Nat16) // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat16, y : Nat16) : Nat16 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.subWrap(1, 2); // => 65535 - /// (1 : Nat16) -% (2 : Nat16) // => 65535 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat16, y : Nat16) : Nat16 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.mulWrap(655, 101); // => 619 - /// (655 : Nat16) *% (101 : Nat16) // => 619 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat16, y : Nat16) : Nat16 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat16.powWrap(2, 16); // => 0 - /// (2 : Nat16) **% (16 : Nat16) // => 0 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat16, y : Nat16) : Nat16 { x **% y }; - -} diff --git a/.mops/base@0.11.1/src/Nat32.mo b/.mops/base@0.11.1/src/Nat32.mo deleted file mode 100644 index 1c92ae5..0000000 --- a/.mops/base@0.11.1/src/Nat32.mo +++ /dev/null @@ -1,586 +0,0 @@ -/// Provides utility functions on 32-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Nat32 "mo:base/Nat32"; -/// ``` -import Nat "Nat"; -import Prim "mo:⛔"; - -module { - - /// 32-bit natural numbers. - public type Nat32 = Prim.Types.Nat32; - - /// Maximum 32-bit natural number. `2 ** 32 - 1`. - /// - /// Example: - /// ```motoko include=import - /// Nat32.maximumValue; // => 4294967295 : Nat32 - /// ``` - public let maximumValue = 4294967295 : Nat32; - - /// Converts a 32-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// Nat32.toNat(123); // => 123 : Nat - /// ``` - public let toNat : Nat32 -> Nat = Prim.nat32ToNat; - - /// Converts an unsigned integer with infinite precision to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// Nat32.fromNat(123); // => 123 : Nat32 - /// ``` - public let fromNat : Nat -> Nat32 = Prim.natToNat32; - - /// Converts a 16-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// Nat32.fromNat16(123); // => 123 : Nat32 - /// ``` - public func fromNat16(x : Nat16) : Nat32 { - Prim.nat16ToNat32(x) - }; - - /// Converts a 32-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// Nat32.toNat16(123); // => 123 : Nat16 - /// ``` - public func toNat16(x : Nat32) : Nat16 { - Prim.nat32ToNat16(x) - }; - - /// Converts a 64-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// Nat32.fromNat64(123); // => 123 : Nat32 - /// ``` - public func fromNat64(x : Nat64) : Nat32 { - Prim.nat64ToNat32(x) - }; - - /// Converts a 32-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// Nat32.toNat64(123); // => 123 : Nat64 - /// ``` - public func toNat64(x : Nat32) : Nat64 { - Prim.nat32ToNat64(x) - }; - - /// Converts a signed integer with infinite precision to a 32-bit unsigned integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Nat32.fromIntWrap(123); // => 123 : Nat32 - /// ``` - public let fromIntWrap : Int -> Nat32 = Prim.intToNat32Wrap; - - /// Converts `x` to its textual representation. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// Nat32.toText(1234); // => "1234" : Text - /// ``` - public func toText(x : Nat32) : Text { - Nat.toText(toNat(x)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat32.min(123, 456); // => 123 : Nat32 - /// ``` - public func min(x : Nat32, y : Nat32) : Nat32 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat32.max(123, 456); // => 456 : Nat32 - /// ``` - public func max(x : Nat32, y : Nat32) : Nat32 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat32 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.equal(1, 1); // => true - /// (1 : Nat32) == (1 : Nat32) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Buffer "mo:base/Buffer"; - /// - /// let buffer1 = Buffer.Buffer(3); - /// let buffer2 = Buffer.Buffer(3); - /// Buffer.equal(buffer1, buffer2, Nat32.equal) // => true - /// ``` - public func equal(x : Nat32, y : Nat32) : Bool { x == y }; - - /// Inequality function for Nat32 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.notEqual(1, 2); // => true - /// (1 : Nat32) != (2 : Nat32) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat32, y : Nat32) : Bool { x != y }; - - /// "Less than" function for Nat32 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.less(1, 2); // => true - /// (1 : Nat32) < (2 : Nat32) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat32, y : Nat32) : Bool { x < y }; - - /// "Less than or equal" function for Nat32 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.lessOrEqual(1, 2); // => true - /// (1 : Nat32) <= (2 : Nat32) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat32, y : Nat32) : Bool { x <= y }; - - /// "Greater than" function for Nat32 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.greater(2, 1); // => true - /// (2 : Nat32) > (1 : Nat32) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat32, y : Nat32) : Bool { x > y }; - - /// "Greater than or equal" function for Nat32 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.greaterOrEqual(2, 1); // => true - /// (2 : Nat32) >= (1 : Nat32) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat32, y : Nat32) : Bool { x >= y }; - - /// General purpose comparison function for `Nat32`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat32.compare(2, 3) // => #less - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.sort([2, 3, 1] : [Nat32], Nat32.compare) // => [1, 2, 3] - /// ``` - public func compare(x : Nat32, y : Nat32) : { #less; #equal; #greater } { - if (x < y) { #less } else if (x == y) { #equal } else { #greater } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.add(1, 2); // => 3 - /// (1 : Nat32) + (2 : Nat32) // => 3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 0, Nat32.add) // => 6 - /// ``` - public func add(x : Nat32, y : Nat32) : Nat32 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.sub(2, 1); // => 1 - /// (2 : Nat32) - (1 : Nat32) // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 20, Nat32.sub) // => 14 - /// ``` - public func sub(x : Nat32, y : Nat32) : Nat32 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.mul(2, 3); // => 6 - /// (2 : Nat32) * (3 : Nat32) // => 6 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 1, Nat32.mul) // => 6 - /// ``` - public func mul(x : Nat32, y : Nat32) : Nat32 { x * y }; - - /// Returns the division of `x by y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.div(6, 2); // => 3 - /// (6 : Nat32) / (2 : Nat32) // => 3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat32, y : Nat32) : Nat32 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.rem(6, 4); // => 2 - /// (6 : Nat32) % (4 : Nat32) // => 2 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat32, y : Nat32) : Nat32 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.pow(2, 3); // => 8 - /// (2 : Nat32) ** (3 : Nat32) // => 8 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat32, y : Nat32) : Nat32 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.bitnot(0) // => 4294967295 - /// ^(0 : Nat32) // => 4294967295 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat32) : Nat32 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.bitand(1, 3); // => 1 - /// (1 : Nat32) & (3 : Nat32) // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat32, y : Nat32) : Nat32 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.bitor(1, 3); // => 3 - /// (1 : Nat32) | (3 : Nat32) // => 3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Nat32, y : Nat32) : Nat32 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.bitxor(1, 3); // => 2 - /// (1 : Nat32) ^ (3 : Nat32) // => 2 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Nat32, y : Nat32) : Nat32 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.bitshiftLeft(1, 3); // => 8 - /// (1 : Nat32) << (3 : Nat32) // => 8 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat32, y : Nat32) : Nat32 { x << y }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.bitshiftRight(8, 3); // => 1 - /// (8 : Nat32) >> (3 : Nat32) // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat32, y : Nat32) : Nat32 { x >> y }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.bitrotLeft(1, 3); // => 8 - /// (1 : Nat32) <<> (3 : Nat32) // => 8 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat32, y : Nat32) : Nat32 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.bitrotRight(1, 1); // => 2147483648 - /// (1 : Nat32) <>> (1 : Nat32) // => 2147483648 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat32, y : Nat32) : Nat32 { x <>> y }; - - /// Returns the value of bit `p mod 32` in `x`, `(x & 2^(p mod 32)) == 2^(p mod 32)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// Nat32.bittest(5, 2); // => true - /// ``` - public func bittest(x : Nat32, p : Nat) : Bool { - Prim.btstNat32(x, Prim.natToNat32(p)) - }; - - /// Returns the value of setting bit `p mod 32` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// Nat32.bitset(5, 1); // => 7 - /// ``` - public func bitset(x : Nat32, p : Nat) : Nat32 { - x | (1 << Prim.natToNat32(p)) - }; - - /// Returns the value of clearing bit `p mod 32` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// Nat32.bitclear(5, 2); // => 1 - /// ``` - public func bitclear(x : Nat32, p : Nat) : Nat32 { - x & ^(1 << Prim.natToNat32(p)) - }; - - /// Returns the value of flipping bit `p mod 32` in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat32.bitflip(5, 2); // => 1 - /// ``` - public func bitflip(x : Nat32, p : Nat) : Nat32 { - x ^ (1 << Prim.natToNat32(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat32.bitcountNonZero(5); // => 2 - /// ``` - public let bitcountNonZero : (x : Nat32) -> Nat32 = Prim.popcntNat32; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat32.bitcountLeadingZero(5); // => 29 - /// ``` - public let bitcountLeadingZero : (x : Nat32) -> Nat32 = Prim.clzNat32; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat32.bitcountTrailingZero(16); // => 4 - /// ``` - public let bitcountTrailingZero : (x : Nat32) -> Nat32 = Prim.ctzNat32; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.addWrap(4294967295, 1); // => 0 - /// (4294967295 : Nat32) +% (1 : Nat32) // => 0 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat32, y : Nat32) : Nat32 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.subWrap(0, 1); // => 4294967295 - /// (0 : Nat32) -% (1 : Nat32) // => 4294967295 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat32, y : Nat32) : Nat32 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.mulWrap(2147483648, 2); // => 0 - /// (2147483648 : Nat32) *% (2 : Nat32) // => 0 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat32, y : Nat32) : Nat32 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat32.powWrap(2, 32); // => 0 - /// (2 : Nat32) **% (32 : Nat32) // => 0 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat32, y : Nat32) : Nat32 { x **% y }; - -} diff --git a/.mops/base@0.11.1/src/Nat64.mo b/.mops/base@0.11.1/src/Nat64.mo deleted file mode 100644 index 3724fce..0000000 --- a/.mops/base@0.11.1/src/Nat64.mo +++ /dev/null @@ -1,565 +0,0 @@ -/// Provides utility functions on 64-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Nat64 "mo:base/Nat64"; -/// ``` -import Nat "Nat"; -import Prim "mo:⛔"; - -module { - - /// 64-bit natural numbers. - public type Nat64 = Prim.Types.Nat64; - - /// Maximum 64-bit natural number. `2 ** 64 - 1`. - /// - /// Example: - /// ```motoko include=import - /// Nat64.maximumValue; // => 18446744073709551615 : Nat64 - /// ``` - - public let maximumValue = 18446744073709551615 : Nat64; - - /// Converts a 64-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// Nat64.toNat(123); // => 123 : Nat - /// ``` - public let toNat : Nat64 -> Nat = Prim.nat64ToNat; - - /// Converts an unsigned integer with infinite precision to a 64-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// Nat64.fromNat(123); // => 123 : Nat64 - /// ``` - public let fromNat : Nat -> Nat64 = Prim.natToNat64; - - /// Converts a 32-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// Nat64.fromNat32(123); // => 123 : Nat64 - /// ``` - public func fromNat32(x : Nat32) : Nat64 { - Prim.nat32ToNat64(x) - }; - - /// Converts a 64-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// Nat64.toNat32(123); // => 123 : Nat32 - /// ``` - public func toNat32(x : Nat64) : Nat32 { - Prim.nat64ToNat32(x) - }; - - /// Converts a signed integer with infinite precision to a 64-bit unsigned integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Nat64.fromIntWrap(123); // => 123 : Nat64 - /// ``` - public let fromIntWrap : Int -> Nat64 = Prim.intToNat64Wrap; - - /// Converts `x` to its textual representation. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// Nat64.toText(1234); // => "1234" : Text - /// ``` - public func toText(x : Nat64) : Text { - Nat.toText(toNat(x)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat64.min(123, 456); // => 123 : Nat64 - /// ``` - public func min(x : Nat64, y : Nat64) : Nat64 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat64.max(123, 456); // => 456 : Nat64 - /// ``` - public func max(x : Nat64, y : Nat64) : Nat64 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat64 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.equal(1, 1); // => true - /// (1 : Nat64) == (1 : Nat64) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Buffer "mo:base/Buffer"; - /// - /// let buffer1 = Buffer.Buffer(3); - /// let buffer2 = Buffer.Buffer(3); - /// Buffer.equal(buffer1, buffer2, Nat64.equal) // => true - /// ``` - public func equal(x : Nat64, y : Nat64) : Bool { x == y }; - - /// Inequality function for Nat64 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.notEqual(1, 2); // => true - /// (1 : Nat64) != (2 : Nat64) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat64, y : Nat64) : Bool { x != y }; - - /// "Less than" function for Nat64 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.less(1, 2); // => true - /// (1 : Nat64) < (2 : Nat64) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat64, y : Nat64) : Bool { x < y }; - - /// "Less than or equal" function for Nat64 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.lessOrEqual(1, 2); // => true - /// (1 : Nat64) <= (2 : Nat64) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat64, y : Nat64) : Bool { x <= y }; - - /// "Greater than" function for Nat64 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.greater(2, 1); // => true - /// (2 : Nat64) > (1 : Nat64) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat64, y : Nat64) : Bool { x > y }; - - /// "Greater than or equal" function for Nat64 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.greaterOrEqual(2, 1); // => true - /// (2 : Nat64) >= (1 : Nat64) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat64, y : Nat64) : Bool { x >= y }; - - /// General purpose comparison function for `Nat64`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat64.compare(2, 3) // => #less - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.sort([2, 3, 1] : [Nat64], Nat64.compare) // => [1, 2, 3] - /// ``` - public func compare(x : Nat64, y : Nat64) : { #less; #equal; #greater } { - if (x < y) { #less } else if (x == y) { #equal } else { #greater } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.add(1, 2); // => 3 - /// (1 : Nat64) + (2 : Nat64) // => 3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 0, Nat64.add) // => 6 - /// ``` - public func add(x : Nat64, y : Nat64) : Nat64 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.sub(3, 1); // => 2 - /// (3 : Nat64) - (1 : Nat64) // => 2 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 10, Nat64.sub) // => 4 - /// ``` - public func sub(x : Nat64, y : Nat64) : Nat64 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.mul(2, 3); // => 6 - /// (2 : Nat64) * (3 : Nat64) // => 6 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 1, Nat64.mul) // => 6 - /// ``` - public func mul(x : Nat64, y : Nat64) : Nat64 { x * y }; - - /// Returns the quotient of `x` divided by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.div(6, 2); // => 3 - /// (6 : Nat64) / (2 : Nat64) // => 3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat64, y : Nat64) : Nat64 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.rem(6, 4); // => 2 - /// (6 : Nat64) % (4 : Nat64) // => 2 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat64, y : Nat64) : Nat64 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.pow(2, 3); // => 8 - /// (2 : Nat64) ** (3 : Nat64) // => 8 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat64, y : Nat64) : Nat64 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.bitnot(0); // => 18446744073709551615 - /// ^(0 : Nat64) // => 18446744073709551615 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat64) : Nat64 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.bitand(1, 3); // => 1 - /// (1 : Nat64) & (3 : Nat64) // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat64, y : Nat64) : Nat64 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.bitor(1, 3); // => 3 - /// (1 : Nat64) | (3 : Nat64) // => 3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Nat64, y : Nat64) : Nat64 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.bitxor(1, 3); // => 2 - /// (1 : Nat64) ^ (3 : Nat64) // => 2 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Nat64, y : Nat64) : Nat64 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.bitshiftLeft(1, 3); // => 8 - /// (1 : Nat64) << (3 : Nat64) // => 8 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat64, y : Nat64) : Nat64 { x << y }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.bitshiftRight(8, 3); // => 1 - /// (8 : Nat64) >> (3 : Nat64) // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat64, y : Nat64) : Nat64 { x >> y }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.bitrotLeft(1, 3); // => 8 - /// (1 : Nat64) <<> (3 : Nat64) // => 8 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat64, y : Nat64) : Nat64 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.bitrotRight(8, 3); // => 1 - /// (8 : Nat64) <>> (3 : Nat64) // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat64, y : Nat64) : Nat64 { x <>> y }; - - /// Returns the value of bit `p mod 64` in `x`, `(x & 2^(p mod 64)) == 2^(p mod 64)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// Nat64.bittest(5, 2); // => true - /// ``` - public func bittest(x : Nat64, p : Nat) : Bool { - Prim.btstNat64(x, Prim.natToNat64(p)) - }; - - /// Returns the value of setting bit `p mod 64` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// Nat64.bitset(5, 1); // => 7 - /// ``` - public func bitset(x : Nat64, p : Nat) : Nat64 { - x | (1 << Prim.natToNat64(p)) - }; - - /// Returns the value of clearing bit `p mod 64` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// Nat64.bitclear(5, 2); // => 1 - /// ``` - public func bitclear(x : Nat64, p : Nat) : Nat64 { - x & ^(1 << Prim.natToNat64(p)) - }; - - /// Returns the value of flipping bit `p mod 64` in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat64.bitflip(5, 2); // => 1 - /// ``` - public func bitflip(x : Nat64, p : Nat) : Nat64 { - x ^ (1 << Prim.natToNat64(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat64.bitcountNonZero(5); // => 2 - /// ``` - public let bitcountNonZero : (x : Nat64) -> Nat64 = Prim.popcntNat64; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat64.bitcountLeadingZero(5); // => 61 - /// ``` - public let bitcountLeadingZero : (x : Nat64) -> Nat64 = Prim.clzNat64; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat64.bitcountTrailingZero(16); // => 4 - /// ``` - public let bitcountTrailingZero : (x : Nat64) -> Nat64 = Prim.ctzNat64; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.addWrap(Nat64.maximumValue, 1); // => 0 - /// Nat64.maximumValue +% (1 : Nat64) // => 0 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat64, y : Nat64) : Nat64 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.subWrap(0, 1); // => 18446744073709551615 - /// (0 : Nat64) -% (1 : Nat64) // => 18446744073709551615 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat64, y : Nat64) : Nat64 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.mulWrap(4294967296, 4294967296); // => 0 - /// (4294967296 : Nat64) *% (4294967296 : Nat64) // => 0 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat64, y : Nat64) : Nat64 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat64.powWrap(2, 64); // => 0 - /// (2 : Nat64) **% (64 : Nat64) // => 0 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat64, y : Nat64) : Nat64 { x **% y }; - -} diff --git a/.mops/base@0.11.1/src/Nat8.mo b/.mops/base@0.11.1/src/Nat8.mo deleted file mode 100644 index 540991d..0000000 --- a/.mops/base@0.11.1/src/Nat8.mo +++ /dev/null @@ -1,559 +0,0 @@ -/// Provides utility functions on 8-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Nat8 "mo:base/Nat8"; -/// ``` -import Nat "Nat"; -import Prim "mo:⛔"; - -module { - - /// 8-bit natural numbers. - public type Nat8 = Prim.Types.Nat8; - - /// Maximum 8-bit natural number. `2 ** 8 - 1`. - /// - /// Example: - /// ```motoko include=import - /// Nat8.maximumValue; // => 255 : Nat8 - /// ``` - public let maximumValue = 255 : Nat8; - - /// Converts an 8-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// Nat8.toNat(123); // => 123 : Nat - /// ``` - public let toNat : Nat8 -> Nat = Prim.nat8ToNat; - - /// Converts an unsigned integer with infinite precision to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// Nat8.fromNat(123); // => 123 : Nat8 - /// ``` - public let fromNat : Nat -> Nat8 = Prim.natToNat8; - - /// Converts a 16-bit unsigned integer to a 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// Nat8.fromNat16(123); // => 123 : Nat8 - /// ``` - public let fromNat16 : Nat16 -> Nat8 = Prim.nat16ToNat8; - - /// Converts an 8-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// Nat8.toNat16(123); // => 123 : Nat16 - /// ``` - public let toNat16 : Nat8 -> Nat16 = Prim.nat8ToNat16; - - /// Converts a signed integer with infinite precision to an 8-bit unsigned integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// Nat8.fromIntWrap(123); // => 123 : Nat8 - /// ``` - public let fromIntWrap : Int -> Nat8 = Prim.intToNat8Wrap; - - /// Converts `x` to its textual representation. - /// - /// Example: - /// ```motoko include=import - /// Nat8.toText(123); // => "123" : Text - /// ``` - public func toText(x : Nat8) : Text { - Nat.toText(toNat(x)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat8.min(123, 200); // => 123 : Nat8 - /// ``` - public func min(x : Nat8, y : Nat8) : Nat8 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat8.max(123, 200); // => 200 : Nat8 - /// ``` - public func max(x : Nat8, y : Nat8) : Nat8 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat8 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.equal(1, 1); // => true - /// (1 : Nat8) == (1 : Nat8) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Buffer "mo:base/Buffer"; - /// - /// let buffer1 = Buffer.Buffer(3); - /// let buffer2 = Buffer.Buffer(3); - /// Buffer.equal(buffer1, buffer2, Nat8.equal) // => true - /// ``` - public func equal(x : Nat8, y : Nat8) : Bool { x == y }; - - /// Inequality function for Nat8 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.notEqual(1, 2); // => true - /// (1 : Nat8) != (2 : Nat8) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat8, y : Nat8) : Bool { x != y }; - - /// "Less than" function for Nat8 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.less(1, 2); // => true - /// (1 : Nat8) < (2 : Nat8) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat8, y : Nat8) : Bool { x < y }; - - /// "Less than or equal" function for Nat8 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat.lessOrEqual(1, 2); // => true - /// 1 <= 2 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat8, y : Nat8) : Bool { x <= y }; - - /// "Greater than" function for Nat8 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.greater(2, 1); // => true - /// (2 : Nat8) > (1 : Nat8) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat8, y : Nat8) : Bool { x > y }; - - /// "Greater than or equal" function for Nat8 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.greaterOrEqual(2, 1); // => true - /// (2 : Nat8) >= (1 : Nat8) // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat8, y : Nat8) : Bool { x >= y }; - - /// General purpose comparison function for `Nat8`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// Nat8.compare(2, 3) // => #less - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.sort([2, 3, 1] : [Nat8], Nat8.compare) // => [1, 2, 3] - /// ``` - public func compare(x : Nat8, y : Nat8) : { #less; #equal; #greater } { - if (x < y) { #less } else if (x == y) { #equal } else { #greater } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.add(1, 2); // => 3 - /// (1 : Nat8) + (2 : Nat8) // => 3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 0, Nat8.add) // => 6 - /// ``` - public func add(x : Nat8, y : Nat8) : Nat8 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.sub(2, 1); // => 1 - /// (2 : Nat8) - (1 : Nat8) // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 20, Nat8.sub) // => 14 - /// ``` - public func sub(x : Nat8, y : Nat8) : Nat8 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.mul(2, 3); // => 6 - /// (2 : Nat8) * (3 : Nat8) // => 6 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:base/Array"; - /// Array.foldLeft([2, 3, 1], 1, Nat8.mul) // => 6 - /// ``` - public func mul(x : Nat8, y : Nat8) : Nat8 { x * y }; - - /// Returns the quotient of `x` divided by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.div(6, 2); // => 3 - /// (6 : Nat8) / (2 : Nat8) // => 3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat8, y : Nat8) : Nat8 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.rem(6, 4); // => 2 - /// (6 : Nat8) % (4 : Nat8) // => 2 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat8, y : Nat8) : Nat8 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.pow(2, 3); // => 8 - /// (2 : Nat8) ** (3 : Nat8) // => 8 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat8, y : Nat8) : Nat8 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.bitnot(0); // => 255 - /// ^(0 : Nat8) // => 255 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat8) : Nat8 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.bitand(3, 2); // => 2 - /// (3 : Nat8) & (2 : Nat8) // => 2 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat8, y : Nat8) : Nat8 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.bitor(3, 2); // => 3 - /// (3 : Nat8) | (2 : Nat8) // => 3 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Nat8, y : Nat8) : Nat8 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.bitxor(3, 2); // => 1 - /// (3 : Nat8) ^ (2 : Nat8) // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Nat8, y : Nat8) : Nat8 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.bitshiftLeft(1, 2); // => 4 - /// (1 : Nat8) << (2 : Nat8) // => 4 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat8, y : Nat8) : Nat8 { x << y }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.bitshiftRight(4, 2); // => 1 - /// (4 : Nat8) >> (2 : Nat8) // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat8, y : Nat8) : Nat8 { x >> y }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.bitrotLeft(128, 1); // => 1 - /// (128 : Nat8) <<> (1 : Nat8) // => 1 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat8, y : Nat8) : Nat8 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.bitrotRight(1, 1); // => 128 - /// (1 : Nat8) <>> (1 : Nat8) // => 128 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat8, y : Nat8) : Nat8 { x <>> y }; - - /// Returns the value of bit `p mod 8` in `x`, `(x & 2^(p mod 8)) == 2^(p mod 8)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// Nat8.bittest(5, 2); // => true - /// ``` - public func bittest(x : Nat8, p : Nat) : Bool { - Prim.btstNat8(x, Prim.natToNat8(p)) - }; - - /// Returns the value of setting bit `p mod 8` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// Nat8.bitset(5, 1); // => 7 - /// ``` - public func bitset(x : Nat8, p : Nat) : Nat8 { - x | (1 << Prim.natToNat8(p)) - }; - - /// Returns the value of clearing bit `p mod 8` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// Nat8.bitclear(5, 2); // => 1 - /// ``` - public func bitclear(x : Nat8, p : Nat) : Nat8 { - x & ^(1 << Prim.natToNat8(p)) - }; - - /// Returns the value of flipping bit `p mod 8` in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat8.bitflip(5, 2); // => 1 - /// ``` - public func bitflip(x : Nat8, p : Nat) : Nat8 { - x ^ (1 << Prim.natToNat8(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat8.bitcountNonZero(5); // => 2 - /// ``` - public let bitcountNonZero : (x : Nat8) -> Nat8 = Prim.popcntNat8; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat8.bitcountLeadingZero(5); // => 5 - /// ``` - public let bitcountLeadingZero : (x : Nat8) -> Nat8 = Prim.clzNat8; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// Nat8.bitcountTrailingZero(6); // => 1 - /// ``` - public let bitcountTrailingZero : (x : Nat8) -> Nat8 = Prim.ctzNat8; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.addWrap(230, 26); // => 0 - /// (230 : Nat8) +% (26 : Nat8) // => 0 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat8, y : Nat8) : Nat8 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.subWrap(0, 1); // => 255 - /// (0 : Nat8) -% (1 : Nat8) // => 255 - /// ``` - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat8, y : Nat8) : Nat8 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.mulWrap(230, 26); // => 92 - /// (230 : Nat8) *% (26 : Nat8) // => 92 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat8, y : Nat8) : Nat8 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// ignore Nat8.powWrap(2, 8); // => 0 - /// (2 : Nat8) **% (8 : Nat8) // => 0 - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat8, y : Nat8) : Nat8 { x **% y }; - -} diff --git a/.mops/base@0.11.1/src/None.mo b/.mops/base@0.11.1/src/None.mo deleted file mode 100644 index b3eaafc..0000000 --- a/.mops/base@0.11.1/src/None.mo +++ /dev/null @@ -1,19 +0,0 @@ -/// The absent value -/// -/// The `None` type represents a type with _no_ value. -/// -/// It is often used to type code that fails to return control (e.g. an infinite loop) -/// or to designate impossible values (e.g. the type `?None` only contains `null`). - -import Prim "mo:⛔"; - -module { - - /// The empty type. A subtype of all types. - public type None = Prim.Types.None; - - /// Turns an absurd value into an arbitrary type. - public let impossible : None -> A = func(x : None) : A { - switch (x) {} - } -} diff --git a/.mops/base@0.11.1/src/Option.mo b/.mops/base@0.11.1/src/Option.mo deleted file mode 100644 index 1af5d4e..0000000 --- a/.mops/base@0.11.1/src/Option.mo +++ /dev/null @@ -1,161 +0,0 @@ -/// Typesafe nulls -/// -/// Optional values can be seen as a typesafe `null`. A value of type `?Int` can -/// be constructed with either `null` or `?42`. The simplest way to get at the -/// contents of an optional is to use pattern matching: -/// -/// ```motoko -/// let optionalInt1 : ?Int = ?42; -/// let optionalInt2 : ?Int = null; -/// -/// let int1orZero : Int = switch optionalInt1 { -/// case null 0; -/// case (?int) int; -/// }; -/// assert int1orZero == 42; -/// -/// let int2orZero : Int = switch optionalInt2 { -/// case null 0; -/// case (?int) int; -/// }; -/// assert int2orZero == 0; -/// ``` -/// -/// The functions in this module capture some common operations when working -/// with optionals that can be more succinct than using pattern matching. - -import P "Prelude"; - -module { - - /// Unwraps an optional value, with a default value, i.e. `get(?x, d) = x` and - /// `get(null, d) = d`. - public func get(x : ?T, default : T) : T = switch x { - case null { default }; - case (?x_) { x_ } - }; - - /// Unwraps an optional value using a function, or returns the default, i.e. - /// `option(?x, f, d) = f x` and `option(null, f, d) = d`. - public func getMapped(x : ?A, f : A -> B, default : B) : B = switch x { - case null { default }; - case (?x_) { f(x_) } - }; - - /// Applies a function to the wrapped value. `null`'s are left untouched. - /// ```motoko - /// import Option "mo:base/Option"; - /// assert Option.map(?42, func x = x + 1) == ?43; - /// assert Option.map(null, func x = x + 1) == null; - /// ``` - public func map(x : ?A, f : A -> B) : ?B = switch x { - case null { null }; - case (?x_) { ?f(x_) } - }; - - /// Applies a function to the wrapped value, but discards the result. Use - /// `iterate` if you're only interested in the side effect `f` produces. - /// - /// ```motoko - /// import Option "mo:base/Option"; - /// var counter : Nat = 0; - /// Option.iterate(?5, func (x : Nat) { counter += x }); - /// assert counter == 5; - /// Option.iterate(null, func (x : Nat) { counter += x }); - /// assert counter == 5; - /// ``` - public func iterate(x : ?A, f : A -> ()) = switch x { - case null {}; - case (?x_) { f(x_) } - }; - - /// Applies an optional function to an optional value. Returns `null` if at - /// least one of the arguments is `null`. - public func apply(x : ?A, f : ?(A -> B)) : ?B { - switch (f, x) { - case (?f_, ?x_) { - ?f_(x_) - }; - case (_, _) { - null - } - } - }; - - /// Applies a function to an optional value. Returns `null` if the argument is - /// `null`, or the function returns `null`. - public func chain(x : ?A, f : A -> ?B) : ?B { - switch (x) { - case (?x_) { - f(x_) - }; - case (null) { - null - } - } - }; - - /// Given an optional optional value, removes one layer of optionality. - /// ```motoko - /// import Option "mo:base/Option"; - /// assert Option.flatten(?(?(42))) == ?42; - /// assert Option.flatten(?(null)) == null; - /// assert Option.flatten(null) == null; - /// ``` - public func flatten(x : ??A) : ?A { - chain( - x, - func(x_ : ?A) : ?A { - x_ - } - ) - }; - - /// Creates an optional value from a definite value. - /// ```motoko - /// import Option "mo:base/Option"; - /// assert Option.make(42) == ?42; - /// ``` - public func make(x : A) : ?A = ?x; - - /// Returns true if the argument is not `null`, otherwise returns false. - public func isSome(x : ?Any) : Bool = switch x { - case null { false }; - case _ { true } - }; - - /// Returns true if the argument is `null`, otherwise returns false. - public func isNull(x : ?Any) : Bool = switch x { - case null { true }; - case _ { false } - }; - - /// Returns true if the optional arguments are equal according to the equality function provided, otherwise returns false. - public func equal(x : ?A, y : ?A, eq : (A, A) -> Bool) : Bool = switch (x, y) { - case (null, null) { true }; - case (?x_, ?y_) { eq(x_, y_) }; - case (_, _) { false } - }; - - /// Asserts that the value is not `null`; fails otherwise. - /// @deprecated Option.assertSome will be removed soon; use an assert expression instead - public func assertSome(x : ?Any) = switch x { - case null { P.unreachable() }; - case _ {} - }; - - /// Asserts that the value _is_ `null`; fails otherwise. - /// @deprecated Option.assertNull will be removed soon; use an assert expression instead - public func assertNull(x : ?Any) = switch x { - case null {}; - case _ { P.unreachable() } - }; - - /// Unwraps an optional value, i.e. `unwrap(?x) = x`. - /// - /// @deprecated Option.unwrap is unsafe and fails if the argument is null; it will be removed soon; use a `switch` or `do?` expression instead - public func unwrap(x : ?T) : T = switch x { - case null { P.unreachable() }; - case (?x_) { x_ } - } -} diff --git a/.mops/base@0.11.1/src/Order.mo b/.mops/base@0.11.1/src/Order.mo deleted file mode 100644 index da271ed..0000000 --- a/.mops/base@0.11.1/src/Order.mo +++ /dev/null @@ -1,46 +0,0 @@ -/// Order - -module { - - /// A type to represent an order. - public type Order = { - #less; - #equal; - #greater - }; - - /// Check if an order is #less. - public func isLess(order : Order) : Bool { - switch order { - case (#less) { true }; - case _ { false } - } - }; - - /// Check if an order is #equal. - public func isEqual(order : Order) : Bool { - switch order { - case (#equal) { true }; - case _ { false } - } - }; - - /// Check if an order is #greater. - public func isGreater(order : Order) : Bool { - switch order { - case (#greater) { true }; - case _ { false } - } - }; - - /// Returns true if only if `o1` and `o2` are the same ordering. - public func equal(o1 : Order, o2 : Order) : Bool { - switch (o1, o2) { - case (#less, #less) { true }; - case (#equal, #equal) { true }; - case (#greater, #greater) { true }; - case _ { false } - } - }; - -} diff --git a/.mops/base@0.11.1/src/Prelude.mo b/.mops/base@0.11.1/src/Prelude.mo deleted file mode 100644 index f16f35d..0000000 --- a/.mops/base@0.11.1/src/Prelude.mo +++ /dev/null @@ -1,33 +0,0 @@ -/// General utilities -/// -/// This prelude file proposes standard library features that _may_ -/// belong in the _language_ (compiler-internal) prelude sometime, after -/// some further experience and discussion. Until then, they live here. - -import Debug "Debug"; - -module { - - /// Not yet implemented - /// - /// Mark incomplete code with the `nyi` and `xxx` functions. - /// - /// Each have calls are well-typed in all typing contexts, which - /// trap in all execution contexts. - public func nyi() : None { - Debug.trap("Prelude.nyi()") - }; - - public func xxx() : None { - Debug.trap("Prelude.xxx()") - }; - - /// Mark unreachable code with the `unreachable` function. - /// - /// Calls are well-typed in all typing contexts, and they - /// trap in all execution contexts. - public func unreachable() : None { - Debug.trap("Prelude.unreachable()") - }; - -} diff --git a/.mops/base@0.11.1/src/Principal.mo b/.mops/base@0.11.1/src/Principal.mo deleted file mode 100644 index d1f1d2d..0000000 --- a/.mops/base@0.11.1/src/Principal.mo +++ /dev/null @@ -1,1222 +0,0 @@ -/// Module for interacting with Principals (users and canisters). -/// -/// Principals are used to identify entities that can interact with the Internet -/// Computer. These entities are either users or canisters. -/// -/// Example textual representation of Principals: -/// -/// `un4fu-tqaaa-aaaab-qadjq-cai` -/// -/// In Motoko, there is a primitive Principal type called `Principal`. As an example -/// of where you might see Principals, you can access the Principal of the -/// caller of your shared function. -/// -/// ```motoko no-repl -/// shared(msg) func foo() { -/// let caller : Principal = msg.caller; -/// }; -/// ``` -/// -/// Then, you can use this module to work with the `Principal`. -/// -/// Import from the base library to use this module. -/// ```motoko name=import -/// import Principal "mo:base/Principal"; -/// ``` - -import Prim "mo:⛔"; -import Blob "Blob"; -import Hash "Hash"; -import Array "Array"; -import Nat8 "Nat8"; -import Nat32 "Nat32"; -import Nat64 "Nat64"; -import Text "Text"; - -module { - - public type Principal = Prim.Types.Principal; - - /// Get the `Principal` identifier of an actor. - /// - /// Example: - /// ```motoko include=import no-repl - /// actor MyCanister { - /// func getPrincipal() : Principal { - /// let principal = Principal.fromActor(MyCanister); - /// } - /// } - /// ``` - public func fromActor(a : actor {}) : Principal = Prim.principalOfActor a; - - /// Compute the Ledger account identifier of a principal. Optionally specify a sub-account. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let subAccount : Blob = "\4A\8D\3F\2B\6E\01\C8\7D\9E\03\B4\56\7C\F8\9A\01\D2\34\56\78\9A\BC\DE\F0\12\34\56\78\9A\BC\DE\F0"; - /// let account = Principal.toLedgerAccount(principal, ?subAccount); // => \8C\5C\20\C6\15\3F\7F\51\E2\0D\0F\0F\B5\08\51\5B\47\65\63\A9\62\B4\A9\91\5F\4F\02\70\8A\ED\4F\82 - /// ``` - public func toLedgerAccount(principal : Principal, subAccount : ?Blob) : Blob { - let sha224 = SHA224(); - let accountSeparator : Blob = "\0Aaccount-id"; - sha224.writeBlob(accountSeparator); - sha224.writeBlob(toBlob(principal)); - switch subAccount { - case (?subAccount) { - sha224.writeBlob(subAccount) - }; - case (null) { - let defaultSubAccount = Array.tabulate(32, func _ = 0); - sha224.writeArray(defaultSubAccount) - } - }; - - let hashSum = sha224.sum(); - - // hashBlob is a CRC32 implementation - let crc32Bytes = nat32ToByteArray(Prim.hashBlob hashSum); - - Blob.fromArray(Array.append(crc32Bytes, Blob.toArray(hashSum))) - }; - - /// Convert a `Principal` to its `Blob` (bytes) representation. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let blob = Principal.toBlob(principal); // => \00\00\00\00\00\30\00\D3\01\01 - /// ``` - public func toBlob(p : Principal) : Blob = Prim.blobOfPrincipal p; - - /// Converts a `Blob` (bytes) representation of a `Principal` to a `Principal` value. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\00\00\00\00\00\30\00\D3\01\01" : Blob; - /// let principal = Principal.fromBlob(blob); - /// Principal.toText(principal) // => "un4fu-tqaaa-aaaab-qadjq-cai" - /// ``` - public func fromBlob(b : Blob) : Principal = Prim.principalOfBlob b; - - /// Converts a `Principal` to its `Text` representation. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// Principal.toText(principal) // => "un4fu-tqaaa-aaaab-qadjq-cai" - /// ``` - public func toText(p : Principal) : Text = debug_show (p); - - /// Converts a `Text` representation of a `Principal` to a `Principal` value. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// Principal.toText(principal) // => "un4fu-tqaaa-aaaab-qadjq-cai" - /// ``` - public func fromText(t : Text) : Principal = fromActor(actor (t)); - - private let anonymousPrincipal : Blob = "\04"; - - /// Checks if the given principal represents an anonymous user. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// Principal.isAnonymous(principal) // => false - /// ``` - public func isAnonymous(p : Principal) : Bool = Prim.blobOfPrincipal p == anonymousPrincipal; - - /// Checks if the given principal can control this canister. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// Principal.isController(principal) // => false - /// ``` - public func isController(p : Principal) : Bool = Prim.isController p; - - /// Hashes the given principal by hashing its `Blob` representation. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// Principal.hash(principal) // => 2_742_573_646 - /// ``` - public func hash(principal : Principal) : Hash.Hash = Blob.hash(Prim.blobOfPrincipal(principal)); - - /// General purpose comparison function for `Principal`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `principal1` with - /// `principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// Principal.compare(principal1, principal2) // => #equal - /// ``` - public func compare(principal1 : Principal, principal2 : Principal) : { - #less; - #equal; - #greater - } { - if (principal1 < principal2) { - #less - } else if (principal1 == principal2) { - #equal - } else { - #greater - } - }; - - /// Equality function for Principal types. - /// This is equivalent to `principal1 == principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.equal(principal1, principal2); - /// principal1 == principal2 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Buffer "mo:base/Buffer"; - /// - /// let buffer1 = Buffer.Buffer(3); - /// let buffer2 = Buffer.Buffer(3); - /// Buffer.equal(buffer1, buffer2, Principal.equal) // => true - /// ``` - public func equal(principal1 : Principal, principal2 : Principal) : Bool { - principal1 == principal2 - }; - - /// Inequality function for Principal types. - /// This is equivalent to `principal1 != principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.notEqual(principal1, principal2); - /// principal1 != principal2 // => false - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(principal1 : Principal, principal2 : Principal) : Bool { - principal1 != principal2 - }; - - /// "Less than" function for Principal types. - /// This is equivalent to `principal1 < principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.less(principal1, principal2); - /// principal1 < principal2 // => false - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(principal1 : Principal, principal2 : Principal) : Bool { - principal1 < principal2 - }; - - /// "Less than or equal to" function for Principal types. - /// This is equivalent to `principal1 <= principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.lessOrEqual(principal1, principal2); - /// principal1 <= principal2 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(principal1 : Principal, principal2 : Principal) : Bool { - principal1 <= principal2 - }; - - /// "Greater than" function for Principal types. - /// This is equivalent to `principal1 > principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.greater(principal1, principal2); - /// principal1 > principal2 // => false - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(principal1 : Principal, principal2 : Principal) : Bool { - principal1 > principal2 - }; - - /// "Greater than or equal to" function for Principal types. - /// This is equivalent to `principal1 >= principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.greaterOrEqual(principal1, principal2); - /// principal1 >= principal2 // => true - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(principal1 : Principal, principal2 : Principal) : Bool { - principal1 >= principal2 - }; - - /** - * SHA224 Utilities used in toAccount(). - * Utilities are not exposed as public functions. - * Taken with permission from https://github.com/research-ag/sha2 - **/ - let K00 : Nat32 = 0x428a2f98; - let K01 : Nat32 = 0x71374491; - let K02 : Nat32 = 0xb5c0fbcf; - let K03 : Nat32 = 0xe9b5dba5; - let K04 : Nat32 = 0x3956c25b; - let K05 : Nat32 = 0x59f111f1; - let K06 : Nat32 = 0x923f82a4; - let K07 : Nat32 = 0xab1c5ed5; - let K08 : Nat32 = 0xd807aa98; - let K09 : Nat32 = 0x12835b01; - let K10 : Nat32 = 0x243185be; - let K11 : Nat32 = 0x550c7dc3; - let K12 : Nat32 = 0x72be5d74; - let K13 : Nat32 = 0x80deb1fe; - let K14 : Nat32 = 0x9bdc06a7; - let K15 : Nat32 = 0xc19bf174; - let K16 : Nat32 = 0xe49b69c1; - let K17 : Nat32 = 0xefbe4786; - let K18 : Nat32 = 0x0fc19dc6; - let K19 : Nat32 = 0x240ca1cc; - let K20 : Nat32 = 0x2de92c6f; - let K21 : Nat32 = 0x4a7484aa; - let K22 : Nat32 = 0x5cb0a9dc; - let K23 : Nat32 = 0x76f988da; - let K24 : Nat32 = 0x983e5152; - let K25 : Nat32 = 0xa831c66d; - let K26 : Nat32 = 0xb00327c8; - let K27 : Nat32 = 0xbf597fc7; - let K28 : Nat32 = 0xc6e00bf3; - let K29 : Nat32 = 0xd5a79147; - let K30 : Nat32 = 0x06ca6351; - let K31 : Nat32 = 0x14292967; - let K32 : Nat32 = 0x27b70a85; - let K33 : Nat32 = 0x2e1b2138; - let K34 : Nat32 = 0x4d2c6dfc; - let K35 : Nat32 = 0x53380d13; - let K36 : Nat32 = 0x650a7354; - let K37 : Nat32 = 0x766a0abb; - let K38 : Nat32 = 0x81c2c92e; - let K39 : Nat32 = 0x92722c85; - let K40 : Nat32 = 0xa2bfe8a1; - let K41 : Nat32 = 0xa81a664b; - let K42 : Nat32 = 0xc24b8b70; - let K43 : Nat32 = 0xc76c51a3; - let K44 : Nat32 = 0xd192e819; - let K45 : Nat32 = 0xd6990624; - let K46 : Nat32 = 0xf40e3585; - let K47 : Nat32 = 0x106aa070; - let K48 : Nat32 = 0x19a4c116; - let K49 : Nat32 = 0x1e376c08; - let K50 : Nat32 = 0x2748774c; - let K51 : Nat32 = 0x34b0bcb5; - let K52 : Nat32 = 0x391c0cb3; - let K53 : Nat32 = 0x4ed8aa4a; - let K54 : Nat32 = 0x5b9cca4f; - let K55 : Nat32 = 0x682e6ff3; - let K56 : Nat32 = 0x748f82ee; - let K57 : Nat32 = 0x78a5636f; - let K58 : Nat32 = 0x84c87814; - let K59 : Nat32 = 0x8cc70208; - let K60 : Nat32 = 0x90befffa; - let K61 : Nat32 = 0xa4506ceb; - let K62 : Nat32 = 0xbef9a3f7; - let K63 : Nat32 = 0xc67178f2; - - let ivs : [[Nat32]] = [ - [ - // 224 - 0xc1059ed8, - 0x367cd507, - 0x3070dd17, - 0xf70e5939, - 0xffc00b31, - 0x68581511, - 0x64f98fa7, - 0xbefa4fa4 - ], - [ - // 256 - 0x6a09e667, - 0xbb67ae85, - 0x3c6ef372, - 0xa54ff53a, - 0x510e527f, - 0x9b05688c, - 0x1f83d9ab, - 0x5be0cd19 - ] - ]; - - let rot = Nat32.bitrotRight; - - class SHA224() { - let (sum_bytes, iv) = (28, 0); - - var s0 : Nat32 = 0; - var s1 : Nat32 = 0; - var s2 : Nat32 = 0; - var s3 : Nat32 = 0; - var s4 : Nat32 = 0; - var s5 : Nat32 = 0; - var s6 : Nat32 = 0; - var s7 : Nat32 = 0; - - let msg : [var Nat32] = Array.init(16, 0); - let digest = Array.init(sum_bytes, 0); - var word : Nat32 = 0; - - var i_msg : Nat8 = 0; - var i_byte : Nat8 = 4; - var i_block : Nat64 = 0; - - public func reset() { - i_msg := 0; - i_byte := 4; - i_block := 0; - s0 := ivs[iv][0]; - s1 := ivs[iv][1]; - s2 := ivs[iv][2]; - s3 := ivs[iv][3]; - s4 := ivs[iv][4]; - s5 := ivs[iv][5]; - s6 := ivs[iv][6]; - s7 := ivs[iv][7] - }; - - reset(); - - private func writeByte(val : Nat8) : () { - word := (word << 8) ^ Nat32.fromIntWrap(Nat8.toNat(val)); - i_byte -%= 1; - if (i_byte == 0) { - msg[Nat8.toNat(i_msg)] := word; - word := 0; - i_byte := 4; - i_msg +%= 1; - if (i_msg == 16) { - process_block(); - i_msg := 0; - i_block +%= 1 - } - } - }; - - private func process_block() : () { - let w00 = msg[0]; - let w01 = msg[1]; - let w02 = msg[2]; - let w03 = msg[3]; - let w04 = msg[4]; - let w05 = msg[5]; - let w06 = msg[6]; - let w07 = msg[7]; - let w08 = msg[8]; - let w09 = msg[9]; - let w10 = msg[10]; - let w11 = msg[11]; - let w12 = msg[12]; - let w13 = msg[13]; - let w14 = msg[14]; - let w15 = msg[15]; - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - /* - for ((i, j, k, l, m) in expansion_rounds.vals()) { - // (j,k,l,m) = (i+1,i+9,i+14,i+16) - let (v0, v1) = (msg[j], msg[l]); - let s0 = rot(v0, 07) ^ rot(v0, 18) ^ (v0 >> 03); - let s1 = rot(v1, 17) ^ rot(v1, 19) ^ (v1 >> 10); - msg[m] := msg[i] +% s0 +% msg[k] +% s1; - }; - */ - // compress - var a = s0; - var b = s1; - var c = s2; - var d = s3; - var e = s4; - var f = s5; - var g = s6; - var h = s7; - var t = 0 : Nat32; - - t := h +% K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K09 +% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K10 +% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K11 +% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K12 +% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K13 +% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K14 +% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - - /* - for (i in compression_rounds.keys()) { - let ch = (e & f) ^ (^ e & g); - let maj = (a & b) ^ (a & c) ^ (b & c); - let sigma0 = rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - let sigma1 = rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - let t = h +% K[i] +% msg[i] +% ch +% sigma1; - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% maj +% sigma0; - }; - */ - // final addition - s0 +%= a; - s1 +%= b; - s2 +%= c; - s3 +%= d; - s4 +%= e; - s5 +%= f; - s6 +%= g; - s7 +%= h - }; - - public func writeIter(iter : { next() : ?Nat8 }) : () { - label reading loop { - switch (iter.next()) { - case (?val) { - writeByte(val); - continue reading - }; - case (null) { - break reading - } - } - } - }; - - public func writeArray(arr : [Nat8]) : () = writeIter(arr.vals()); - public func writeBlob(blob : Blob) : () = writeIter(blob.vals()); - - public func sum() : Blob { - // calculate padding - // t = bytes in the last incomplete block (0-63) - let t : Nat8 = (i_msg << 2) +% 4 -% i_byte; - // p = length of padding (1-64) - var p : Nat8 = if (t < 56) (56 -% t) else (120 -% t); - // n_bits = length of message in bits - let n_bits : Nat64 = ((i_block << 6) +% Nat64.fromIntWrap(Nat8.toNat(t))) << 3; - - // write padding - writeByte(0x80); - p -%= 1; - while (p != 0) { - writeByte(0x00); - p -%= 1 - }; - - // write length (8 bytes) - // Note: this exactly fills the block buffer, hence process_block will get - // triggered by the last writeByte - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 56) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 48) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 40) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 32) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 24) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 16) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 8) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat(n_bits & 0xff))); - - // retrieve sum - digest[0] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 24) & 0xff)); - digest[1] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 16) & 0xff)); - digest[2] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 8) & 0xff)); - digest[3] := Nat8.fromIntWrap(Nat32.toNat(s0 & 0xff)); - digest[4] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 24) & 0xff)); - digest[5] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 16) & 0xff)); - digest[6] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 8) & 0xff)); - digest[7] := Nat8.fromIntWrap(Nat32.toNat(s1 & 0xff)); - digest[8] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 24) & 0xff)); - digest[9] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 16) & 0xff)); - digest[10] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 8) & 0xff)); - digest[11] := Nat8.fromIntWrap(Nat32.toNat(s2 & 0xff)); - digest[12] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 24) & 0xff)); - digest[13] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 16) & 0xff)); - digest[14] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 8) & 0xff)); - digest[15] := Nat8.fromIntWrap(Nat32.toNat(s3 & 0xff)); - digest[16] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 24) & 0xff)); - digest[17] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 16) & 0xff)); - digest[18] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 8) & 0xff)); - digest[19] := Nat8.fromIntWrap(Nat32.toNat(s4 & 0xff)); - digest[20] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 24) & 0xff)); - digest[21] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 16) & 0xff)); - digest[22] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 8) & 0xff)); - digest[23] := Nat8.fromIntWrap(Nat32.toNat(s5 & 0xff)); - digest[24] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 24) & 0xff)); - digest[25] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 16) & 0xff)); - digest[26] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 8) & 0xff)); - digest[27] := Nat8.fromIntWrap(Nat32.toNat(s6 & 0xff)); - - return Blob.fromArrayMut(digest) - } - }; // class SHA224 - - func nat32ToByteArray(n : Nat32) : [Nat8] { - func byte(n : Nat32) : Nat8 { - Nat8.fromNat(Nat32.toNat(n & 0xff)) - }; - [byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)] - } -} diff --git a/.mops/base@0.11.1/src/RBTree.mo b/.mops/base@0.11.1/src/RBTree.mo deleted file mode 100644 index c511fc7..0000000 --- a/.mops/base@0.11.1/src/RBTree.mo +++ /dev/null @@ -1,681 +0,0 @@ -/// Key-value map implemented as a red-black tree (RBTree) with nodes storing key-value pairs. -/// -/// A red-black tree is a balanced binary search tree ordered by the keys. -/// -/// The tree data structure internally colors each of its nodes either red or black, -/// and uses this information to balance the tree during the modifying operations. -/// -/// Creation: -/// Instantiate class `RBTree` that provides a map from keys of type `K` to values of type `V`. -/// -/// Example: -/// ```motoko -/// import RBTree "mo:base/RBTree"; -/// import Nat "mo:base/Nat"; -/// import Debug "mo:base/Debug"; -/// -/// let tree = RBTree.RBTree(Nat.compare); // Create a new red-black tree mapping Nat to Text -/// tree.put(1, "one"); -/// tree.put(2, "two"); -/// tree.put(3, "tree"); -/// for (entry in tree.entries()) { -/// Debug.print("Entry key=" # debug_show(entry.0) # " value=\"" # entry.1 #"\""); -/// } -/// ``` -/// -/// Performance: -/// * Runtime: `O(log(n))` worst case cost per insertion, removal, and retrieval operation. -/// * Space: `O(n)` for storing the entire tree. -/// `n` denotes the number of key-value entries (i.e. nodes) stored in the tree. -/// -/// Note: -/// * Tree operations, such as retrieval, insertion, and removal create `O(log(n))` temporary objects that become garbage. -/// -/// Credits: -/// -/// The core of this implementation is derived from: -/// -/// * Ken Friis Larsen's [RedBlackMap.sml](https://github.com/kfl/mosml/blob/master/src/mosmllib/Redblackmap.sml), which itself is based on: -/// * Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](http://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html). - - -import Debug "Debug"; -import I "Iter"; -import List "List"; -import Nat "Nat"; -import O "Order"; - -// TODO: a faster, more compact and less indirect representation would be: -// type Tree = { -// #red : (Tree, K, V, Tree); -// #black : (Tree, K, V, Tree); -// #leaf -//}; -// (this inlines the colors into the variant, flattens a tuple, and removes a (now) redundant optin, for considerable heap savings.) -// It would also make sense to maintain the size in a separate root for 0(1) access. - -// FUTURE: deprecate RBTree.mo and replace by RedBlackMap.mo, using this new representation - -module { - - /// Node color: Either red (`#R`) or black (`#B`). - public type Color = { #R; #B }; - - /// Red-black tree of nodes with key-value entries, ordered by the keys. - /// The keys have the generic type `K` and the values the generic type `V`. - /// Leaves are considered implicitly black. - public type Tree = { - #node : (Color, Tree, (K, ?V), Tree); - #leaf - }; - - - - /// A map from keys of type `K` to values of type `V` implemented as a red-black tree. - /// The entries of key-value pairs are ordered by `compare` function applied to the keys. - /// - /// The class enables imperative usage in object-oriented-style. - /// However, internally, the class uses a functional implementation. - /// - /// The `compare` function should implement a consistent total order among all possible values of `K` and - /// for efficiency, only involves `O(1)` runtime costs without space allocation. - /// - /// Example: - /// ```motoko name=initialize - /// import RBTree "mo:base/RBTree"; - /// import Nat "mo:base/Nat"; - /// - /// let tree = RBTree.RBTree(Nat.compare); // Create a map of `Nat` to `Text` using the `Nat.compare` order - /// ``` - /// - /// Costs of instantiation (only empty tree): - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public class RBTree(compare : (K, K) -> O.Order) { - - var tree : Tree = (#leaf : Tree); - - /// Return a snapshot of the internal functional tree representation as sharable data. - /// The returned tree representation is not affected by subsequent changes of the `RBTree` instance. - /// - /// - /// Example: - /// ```motoko include=initialize - /// - /// tree.put(1, "one"); - /// let treeSnapshot = tree.share(); - /// tree.put(2, "second"); - /// RBTree.size(treeSnapshot) // => 1 (Only the first insertion is part of the snapshot.) - /// ``` - /// - /// Useful for storing the state of a tree object as a stable variable, determining its size, pretty-printing, and sharing it across async function calls, - /// i.e. passing it in async arguments or async results. - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func share() : Tree { - tree - }; - - /// Reset the current state of the tree object from a functional tree representation. - /// - /// Example: - /// ```motoko include=initialize - /// import Iter "mo:base/Iter"; - /// - /// tree.put(1, "one"); - /// let snapshot = tree.share(); // save the current state of the tree object in a snapshot - /// tree.put(2, "two"); - /// tree.unshare(snapshot); // restore the tree object from the snapshot - /// Iter.toArray(tree.entries()) // => [(1, "one")] - /// ``` - /// - /// Useful for restoring the state of a tree object from stable data, saved, for example, in a stable variable. - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func unshare(t : Tree) : () { - tree := t - }; - - - /// Retrieve the value associated with a given key, if present. Returns `null`, if the key is absent. - /// The key is searched according to the `compare` function defined on the class instantiation. - /// - /// Example: - /// ```motoko include=initialize - /// - /// tree.put(1, "one"); - /// tree.put(2, "two"); - /// - /// tree.get(1) // => ?"one" - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the tree and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func get(key : K) : ?V { - getRec(key, compare, tree) - }; - - /// Replace the value associated with a given key, if the key is present. - /// Otherwise, if the key does not yet exist, insert the key-value entry. - /// - /// Returns the previous value of the key, if the key already existed. - /// Otherwise, `null`, if the key did not yet exist before. - /// - /// Example: - /// ```motoko include=initialize - /// import Iter "mo:base/Iter"; - /// - /// tree.put(1, "old one"); - /// tree.put(2, "two"); - /// - /// ignore tree.replace(1, "new one"); - /// Iter.toArray(tree.entries()) // => [(1, "new one"), (2, "two")] - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the tree and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func replace(key : K, value : V) : ?V { - let (t, res) = insert(tree, compare, key, value); - tree := t; - res - }; - - /// Insert a key-value entry in the tree. If the key already exists, it overwrites the associated value. - /// - /// Example: - /// ```motoko include=initialize - /// import Iter "mo:base/Iter"; - /// - /// tree.put(1, "one"); - /// tree.put(2, "two"); - /// tree.put(3, "three"); - /// Iter.toArray(tree.entries()) // now contains three entries - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the tree and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func put(key : K, value : V) { - let (t, _res) = insert(tree, compare, key, value); - tree := t - }; - - /// Delete the entry associated with a given key, if the key exists. - /// No effect if the key is absent. Same as `remove(key)` except that it - /// does not have a return value. - /// - /// Example: - /// ```motoko include=initialize - /// import Iter "mo:base/Iter"; - /// - /// tree.put(1, "one"); - /// tree.put(2, "two"); - /// - /// tree.delete(1); - /// Iter.toArray(tree.entries()) // => [(2, "two")]. - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the tree and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func delete(key : K) { - let (_res, t) = removeRec(key, compare, tree); - tree := t - }; - - /// Remove the entry associated with a given key, if the key exists, and return the associated value. - /// Returns `null` without any other effect if the key is absent. - /// - /// Example: - /// ```motoko include=initialize - /// import Iter "mo:base/Iter"; - /// - /// tree.put(1, "one"); - /// tree.put(2, "two"); - /// - /// ignore tree.remove(1); - /// Iter.toArray(tree.entries()) // => [(2, "two")]. - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the tree and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func remove(key : K) : ?V { - let (res, t) = removeRec(key, compare, tree); - tree := t; - res - }; - - /// An iterator for the key-value entries of the map, in ascending key order. - /// The iterator takes a snapshot view of the tree and is not affected by concurrent modifications. - /// - /// Example: - /// ```motoko include=initialize - /// import Debug "mo:base/Debug"; - /// - /// tree.put(1, "one"); - /// tree.put(2, "two"); - /// tree.put(3, "two"); - /// - /// for (entry in tree.entries()) { - /// Debug.print("Entry key=" # debug_show(entry.0) # " value=\"" # entry.1 #"\""); - /// } - /// - /// // Entry key=1 value="one" - /// // Entry key=2 value="two" - /// // Entry key=3 value="three" - /// ``` - /// - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the tree. - /// - /// Note: Full tree iteration creates `O(n)` temporary objects that will be collected as garbage. - public func entries() : I.Iter<(K, V)> { iter(tree, #fwd) }; - - /// An iterator for the key-value entries of the map, in descending key order. - /// The iterator takes a snapshot view of the tree and is not affected by concurrent modifications. - /// - /// Example: - /// ```motoko include=initialize - /// import Debug "mo:base/Debug"; - /// - /// let tree = RBTree.RBTree(Nat.compare); - /// tree.put(1, "one"); - /// tree.put(2, "two"); - /// tree.put(3, "two"); - /// - /// for (entry in tree.entriesRev()) { - /// Debug.print("Entry key=" # debug_show(entry.0) # " value=\"" # entry.1 #"\""); - /// } - /// - /// // Entry key=3 value="three" - /// // Entry key=2 value="two" - /// // Entry key=1 value="one" - /// ``` - /// - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the tree. - /// - /// Note: Full tree iteration creates `O(n)` temporary objects that will be collected as garbage. - public func entriesRev() : I.Iter<(K, V)> { iter(tree, #bwd) }; - - }; // end class - - type IterRep = List.List<{ #tr : Tree; #xy : (X, ?Y) }>; - - /// Get an iterator for the entries of the `tree`, in ascending (`#fwd`) or descending (`#bwd`) order as specified by `direction`. - /// The iterator takes a snapshot view of the tree and is not affected by concurrent modifications. - /// - /// Example: - /// ```motoko - /// import RBTree "mo:base/RBTree"; - /// import Nat "mo:base/Nat"; - /// import Debug "mo:base/Debug"; - /// - /// let tree = RBTree.RBTree(Nat.compare); - /// tree.put(1, "one"); - /// tree.put(2, "two"); - /// tree.put(3, "two"); - /// - /// for (entry in RBTree.iter(tree.share(), #bwd)) { // backward iteration - /// Debug.print("Entry key=" # debug_show(entry.0) # " value=\"" # entry.1 #"\""); - /// } - /// - /// // Entry key=3 value="three" - /// // Entry key=2 value="two" - /// // Entry key=1 value="one" - /// ``` - /// - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the tree. - /// - /// Note: Full tree iteration creates `O(n)` temporary objects that will be collected as garbage. - public func iter(tree : Tree, direction : { #fwd; #bwd }) : I.Iter<(X, Y)> { - object { - var trees : IterRep = ?(#tr(tree), null); - public func next() : ?(X, Y) { - switch (direction, trees) { - case (_, null) { null }; - case (_, ?(#tr(#leaf), ts)) { - trees := ts; - next() - }; - case (_, ?(#xy(xy), ts)) { - trees := ts; - switch (xy.1) { - case null { next() }; - case (?y) { ?(xy.0, y) } - } - }; - case (#fwd, ?(#tr(#node(_, l, xy, r)), ts)) { - trees := ?(#tr(l), ?(#xy(xy), ?(#tr(r), ts))); - next() - }; - case (#bwd, ?(#tr(#node(_, l, xy, r)), ts)) { - trees := ?(#tr(r), ?(#xy(xy), ?(#tr(l), ts))); - next() - } - } - } - } - }; - - /// Remove the value associated with a given key. - func removeRec(x : X, compare : (X, X) -> O.Order, t : Tree) : (?Y, Tree) { - let (t1, r) = remove(t, compare, x); - (r, t1); - }; - - func getRec(x : X, compare : (X, X) -> O.Order, t : Tree) : ?Y { - switch t { - case (#leaf) { null }; - case (#node(c, l, xy, r)) { - switch (compare(x, xy.0)) { - case (#less) { getRec(x, compare, l) }; - case (#equal) { xy.1 }; - case (#greater) { getRec(x, compare, r) } - } - } - } - }; - - /// Determine the size of the tree as the number of key-value entries. - /// - /// Example: - /// ```motoko - /// import RBTree "mo:base/RBTree"; - /// import Nat "mo:base/Nat"; - /// - /// let tree = RBTree.RBTree(Nat.compare); - /// tree.put(1, "one"); - /// tree.put(2, "two"); - /// tree.put(3, "three"); - /// - /// RBTree.size(tree.share()) // 3 entries - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the tree. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func size(t : Tree) : Nat { - switch t { - case (#leaf) { 0 }; - case (#node(_, l, xy, r)) { - size(l) + size(r) + (switch (xy.1) { case null 0; case _ 1 }) - } - } - }; - - func redden(t : Tree) : Tree { - switch t { - case (#node (#B, l, xy, r)) { - (#node (#R, l, xy, r)) - }; - case _ { - Debug.trap "RBTree.red" - } - } - }; - - func lbalance(left : Tree, xy : (X,?Y), right : Tree) : Tree { - switch (left, right) { - case (#node(#R, #node(#R, l1, xy1, r1), xy2, r2), r) { - #node( - #R, - #node(#B, l1, xy1, r1), - xy2, - #node(#B, r2, xy, r)) - }; - case (#node(#R, l1, xy1, #node(#R, l2, xy2, r2)), r) { - #node( - #R, - #node(#B, l1, xy1, l2), - xy2, - #node(#B, r2, xy, r)) - }; - case _ { - #node(#B, left, xy, right) - } - } - }; - - func rbalance(left : Tree, xy : (X,?Y), right : Tree) : Tree { - switch (left, right) { - case (l, #node(#R, l1, xy1, #node(#R, l2, xy2, r2))) { - #node( - #R, - #node(#B, l, xy, l1), - xy1, - #node(#B, l2, xy2, r2)) - }; - case (l, #node(#R, #node(#R, l1, xy1, r1), xy2, r2)) { - #node( - #R, - #node(#B, l, xy, l1), - xy1, - #node(#B, r1, xy2, r2)) - }; - case _ { - #node(#B, left, xy, right) - }; - } - }; - - func insert( - tree : Tree, - compare : (X, X) -> O.Order, - x : X, - y : Y - ) - : (Tree, ?Y) { - var y0 : ?Y = null; - func ins(tree : Tree) : Tree { - switch tree { - case (#leaf) { - #node(#R, #leaf, (x,?y), #leaf) - }; - case (#node(#B, left, xy, right)) { - switch (compare (x, xy.0)) { - case (#less) { - lbalance(ins left, xy, right) - }; - case (#greater) { - rbalance(left, xy, ins right) - }; - case (#equal) { - y0 := xy.1; - #node(#B, left, (x,?y), right) - } - } - }; - case (#node(#R, left, xy, right)) { - switch (compare (x, xy.0)) { - case (#less) { - #node(#R, ins left, xy, right) - }; - case (#greater) { - #node(#R, left, xy, ins right) - }; - case (#equal) { - y0 := xy.1; - #node(#R, left, (x,?y), right) - } - } - } - }; - }; - switch (ins tree) { - case (#node(#R, left, xy, right)) { - (#node(#B, left, xy, right), y0); - }; - case other { (other, y0) }; - }; - }; - - - func balLeft(left : Tree, xy : (X,?Y), right : Tree) : Tree { - switch (left, right) { - case (#node(#R, l1, xy1, r1), r) { - #node( - #R, - #node(#B, l1, xy1, r1), - xy, - r) - }; - case (_, #node(#B, l2, xy2, r2)) { - rbalance(left, xy, #node(#R, l2, xy2, r2)) - }; - case (_, #node(#R, #node(#B, l2, xy2, r2), xy3, r3)) { - #node(#R, - #node(#B, left, xy, l2), - xy2, - rbalance(r2, xy3, redden r3)) - }; - case _ { Debug.trap "balLeft" }; - } - }; - - func balRight(left : Tree, xy : (X,?Y), right : Tree) : Tree { - switch (left, right) { - case (l, #node(#R, l1, xy1, r1)) { - #node(#R, - l, - xy, - #node(#B, l1, xy1, r1)) - }; - case (#node(#B, l1, xy1, r1), r) { - lbalance(#node(#R, l1, xy1, r1), xy, r); - }; - case (#node(#R, l1, xy1, #node(#B, l2, xy2, r2)), r3) { - #node(#R, - lbalance(redden l1, xy1, l2), - xy2, - #node(#B, r2, xy, r3)) - }; - case _ { Debug.trap "balRight" }; - } - }; - - func append(left : Tree, right: Tree) : Tree { - switch (left, right) { - case (#leaf, _) { right }; - case (_, #leaf) { left }; - case (#node (#R, l1, xy1, r1), - #node (#R, l2, xy2, r2)) { - switch (append (r1, l2)) { - case (#node (#R, l3, xy3, r3)) { - #node( - #R, - #node(#R, l1, xy1, l3), - xy3, - #node(#R, r3, xy2, r2)) - }; - case r1l2 { - #node(#R, l1, xy1, #node(#R, r1l2, xy2, r2)) - } - } - }; - case (t1, #node(#R, l2, xy2, r2)) { - #node(#R, append(t1, l2), xy2, r2) - }; - case (#node(#R, l1, xy1, r1), t2) { - #node(#R, l1, xy1, append(r1, t2)) - }; - case (#node(#B, l1, xy1, r1), #node (#B, l2, xy2, r2)) { - switch (append (r1, l2)) { - case (#node (#R, l3, xy3, r3)) { - #node(#R, - #node(#B, l1, xy1, l3), - xy3, - #node(#B, r3, xy2, r2)) - }; - case r1l2 { - balLeft ( - l1, - xy1, - #node(#B, r1l2, xy2, r2) - ) - } - } - } - } - }; - - func remove(tree : Tree, compare : (X, X) -> O.Order, x : X) : (Tree, ?Y) { - var y0 : ?Y = null; - func delNode(left : Tree, xy : (X, ?Y), right : Tree) : Tree { - switch (compare (x, xy.0)) { - case (#less) { - let newLeft = del left; - switch left { - case (#node(#B, _, _, _)) { - balLeft(newLeft, xy, right) - }; - case _ { - #node(#R, newLeft, xy, right) - } - } - }; - case (#greater) { - let newRight = del right; - switch right { - case (#node(#B, _, _, _)) { - balRight(left, xy, newRight) - }; - case _ { - #node(#R, left, xy, newRight) - } - } - }; - case (#equal) { - y0 := xy.1; - append(left, right) - }; - } - }; - func del(tree : Tree) : Tree { - switch tree { - case (#leaf) { - tree - }; - case (#node(_, left, xy, right)) { - delNode(left, xy, right) - } - }; - }; - switch (del(tree)) { - case (#node(#R, left, xy, right)) { - (#node(#B, left, xy, right), y0); - }; - case other { (other, y0) }; - }; - } - -} diff --git a/.mops/base@0.11.1/src/Random.mo b/.mops/base@0.11.1/src/Random.mo deleted file mode 100644 index b4c3839..0000000 --- a/.mops/base@0.11.1/src/Random.mo +++ /dev/null @@ -1,270 +0,0 @@ -/// A module for obtaining randomness on the Internet Computer (IC). -/// -/// This module provides the fundamentals for user abstractions to build on. -/// -/// Dealing with randomness on a deterministic computing platform, such -/// as the IC, is intricate. Some basic rules need to be followed by the -/// user of this module to obtain (and maintain) the benefits of crypto- -/// graphic randomness: -/// -/// - cryptographic entropy (randomness source) is only obtainable -/// asyncronously in discrete chunks of 256 bits (32-byte sized `Blob`s) -/// - all bets must be closed *before* entropy is being asked for in -/// order to decide them -/// - this implies that the same entropy (i.e. `Blob`) - or surplus entropy -/// not utilised yet - cannot be used for a new round of bets without -/// losing the cryptographic guarantees. -/// -/// Concretely, the below class `Finite`, as well as the -/// `*From` methods risk the carrying-over of state from previous rounds. -/// These are provided for performance (and convenience) reasons, and need -/// special care when used. Similar caveats apply for user-defined (pseudo) -/// random number generators. -/// -/// Usage: -/// ```motoko no-repl -/// import Random "mo:base/Random"; -/// ``` - -import I "Iter"; -import Option "Option"; -import Prim "mo:⛔"; - -module { - - let raw_rand = (actor "aaaaa-aa" : actor { raw_rand : () -> async Blob }).raw_rand; - - /// Obtains a full blob (32 bytes) worth of fresh entropy. - /// - /// Example: - /// ```motoko no-repl - /// let random = Random.Finite(await Random.blob()); - /// ``` - public let blob : shared () -> async Blob = raw_rand; - - /// Drawing from a finite supply of entropy, `Finite` provides - /// methods to obtain random values. When the entropy is used up, - /// `null` is returned. Otherwise the outcomes' distributions are - /// stated for each method. The uniformity of outcomes is - /// guaranteed only when the supplied entropy is originally obtained - /// by the `blob()` call, and is never reused. - /// - /// Example: - /// ```motoko no-repl - /// import Random "mo:base/Random"; - /// - /// let random = Random.Finite(await Random.blob()); - /// - /// let seed : Blob = "\14\C9\72\09\03\D4\D5\72\82\95\E5\43\AF\FA\A9\44\49\2F\25\56\13\F3\6E\C7\B0\87\DC\76\08\69\14\CF"; - /// let seedRandom = Random.Finite(seed); - /// ``` - public class Finite(entropy : Blob) { - let it : I.Iter = entropy.vals(); - - /// Uniformly distributes outcomes in the numeric range [0 .. 255]. - /// Consumes 1 byte of entropy. - /// - /// Example: - /// ```motoko no-repl - /// let seed : Blob = "\14\C9\72\09\03\D4\D5\72\82\95\E5\43\AF\FA\A9\44\49\2F\25\56\13\F3\6E\C7\B0\87\DC\76\08\69\14\CF"; - /// let random = Random.Finite(seed); - /// random.byte() // => ?20 - /// ``` - public func byte() : ?Nat8 { - it.next() - }; - - /// Bool iterator splitting up a byte of entropy into 8 bits - let bit : I.Iter = object { - var mask = 0x00 : Nat8; - var byte = 0x00 : Nat8; - public func next() : ?Bool { - if (0 : Nat8 == mask) { - switch (it.next()) { - case null { null }; - case (?w) { - byte := w; - mask := 0x40; - ?(0 : Nat8 != byte & (0x80 : Nat8)) - } - } - } else { - let m = mask; - mask >>= (1 : Nat8); - ?(0 : Nat8 != byte & m) - } - } - }; - - /// Simulates a coin toss. Both outcomes have equal probability. - /// Consumes 1 bit of entropy (amortised). - /// - /// Example: - /// ```motoko no-repl - /// let seed : Blob = "\14\C9\72\09\03\D4\D5\72\82\95\E5\43\AF\FA\A9\44\49\2F\25\56\13\F3\6E\C7\B0\87\DC\76\08\69\14\CF"; - /// let random = Random.Finite(seed); - /// random.coin() // => ?false - /// ``` - public func coin() : ?Bool { - bit.next() - }; - - /// Uniformly distributes outcomes in the numeric range [0 .. 2^p - 1]. - /// Consumes ⌈p/8⌉ bytes of entropy. - /// - /// Example: - /// ```motoko no-repl - /// let seed : Blob = "\14\C9\72\09\03\D4\D5\72\82\95\E5\43\AF\FA\A9\44\49\2F\25\56\13\F3\6E\C7\B0\87\DC\76\08\69\14\CF"; - /// let random = Random.Finite(seed); - /// random.range(32) // => ?348746249 - /// ``` - public func range(p : Nat8) : ?Nat { - var pp = p; - var acc : Nat = 0; - for (i in it) { - if (8 : Nat8 <= pp) { - acc := acc * 256 + Prim.nat8ToNat(i) - } - else if (0 : Nat8 == pp) { - return ?acc - } else { - acc *= Prim.nat8ToNat(1 << pp); - let mask : Nat8 = 0xff >> (8 - pp); - return ?(acc + Prim.nat8ToNat(i & mask)) - }; - pp -= 8 - }; - if (0 : Nat8 == pp) - ?acc - else null - }; - - /// Counts the number of heads in `n` fair coin tosses. - /// Consumes ⌈n/8⌉ bytes of entropy. - /// - /// Example: - /// ```motoko no-repl - /// let seed : Blob = "\14\C9\72\09\03\D4\D5\72\82\95\E5\43\AF\FA\A9\44\49\2F\25\56\13\F3\6E\C7\B0\87\DC\76\08\69\14\CF"; - /// let random = Random.Finite(seed); - /// random.binomial(5) // => ?1 - /// ``` - public func binomial(n : Nat8) : ?Nat8 { - var nn = n; - var acc : Nat8 = 0; - for (i in it) { - if (8 : Nat8 <= nn) { - acc +%= Prim.popcntNat8(i) - } else if (0 : Nat8 == nn) { - return ?acc - } else { - let mask : Nat8 = 0xff << (8 - nn); - let residue = Prim.popcntNat8(i & mask); - return ?(acc +% residue) - }; - nn -= 8 - }; - if (0 : Nat8 == nn) - ?acc - else null - } - }; - - /// Distributes outcomes in the numeric range [0 .. 255]. - /// Seed blob must contain at least a byte. - /// - /// Example: - /// ```motoko no-repl - /// let seed : Blob = "\14\C9\72\09\03\D4\D5\72\82\95\E5\43\AF\FA\A9\44\49\2F\25\56\13\F3\6E\C7\B0\87\DC\76\08\69\14\CF"; - /// Random.byteFrom(seed) // => 20 - /// ``` - public func byteFrom(seed : Blob) : Nat8 { - switch (seed.vals().next()) { - case (?w) { w }; - case _ { Prim.trap "Random.byteFrom" } - } - }; - - /// Simulates a coin toss. - /// Seed blob must contain at least a byte. - /// - /// Example: - /// ```motoko no-repl - /// let seed : Blob = "\14\C9\72\09\03\D4\D5\72\82\95\E5\43\AF\FA\A9\44\49\2F\25\56\13\F3\6E\C7\B0\87\DC\76\08\69\14\CF"; - /// Random.coinFrom(seed) // => false - /// ``` - public func coinFrom(seed : Blob) : Bool { - switch (seed.vals().next()) { - case (?w) { w > (127 : Nat8) }; - case _ { Prim.trap "Random.coinFrom" } - } - }; - - /// Distributes outcomes in the numeric range [0 .. 2^p - 1]. - /// Seed blob must contain at least ((p+7) / 8) bytes. - /// - /// Example: - /// ```motoko no-repl - /// let seed : Blob = "\14\C9\72\09\03\D4\D5\72\82\95\E5\43\AF\FA\A9\44\49\2F\25\56\13\F3\6E\C7\B0\87\DC\76\08\69\14\CF"; - /// Random.rangeFrom(32, seed) // => 348746249 - /// ``` - public func rangeFrom(p : Nat8, seed : Blob) : Nat { - rangeIter(p, seed.vals()) - }; - - // internal worker method, expects iterator with sufficient supply - func rangeIter(p : Nat8, it : I.Iter) : Nat { - var pp = p; - var acc : Nat = 0; - for (i in it) { - if (8 : Nat8 <= pp) { - acc := acc * 256 + Prim.nat8ToNat(i) - } else if (0 : Nat8 == pp) { - return acc - } else { - acc *= Prim.nat8ToNat(1 << pp); - let mask : Nat8 = 0xff >> (8 - pp); - return acc + Prim.nat8ToNat(i & mask) - }; - pp -= 8 - }; - if (0 : Nat8 == pp) { - return acc - } - else Prim.trap("Random.rangeFrom") - }; - - /// Counts the number of heads in `n` coin tosses. - /// Seed blob must contain at least ((n+7) / 8) bytes. - /// - /// Example: - /// ```motoko no-repl - /// let seed : Blob = "\14\C9\72\09\03\D4\D5\72\82\95\E5\43\AF\FA\A9\44\49\2F\25\56\13\F3\6E\C7\B0\87\DC\76\08\69\14\CF"; - /// Random.binomialFrom(5, seed) // => 1 - /// ``` - public func binomialFrom(n : Nat8, seed : Blob) : Nat8 { - binomialIter(n, seed.vals()) - }; - - // internal worker method, expects iterator with sufficient supply - func binomialIter(n : Nat8, it : I.Iter) : Nat8 { - var nn = n; - var acc : Nat8 = 0; - for (i in it) { - if (8 : Nat8 <= nn) { - acc +%= Prim.popcntNat8(i) - } else if (0 : Nat8 == nn) { - return acc - } else { - let mask : Nat8 = 0xff << (8 - nn); - let residue = Prim.popcntNat8(i & mask); - return (acc +% residue) - }; - nn -= 8 - }; - if (0 : Nat8 == nn) { - return acc - } - else Prim.trap("Random.binomialFrom") - } - -} diff --git a/.mops/base@0.11.1/src/Region.mo b/.mops/base@0.11.1/src/Region.mo deleted file mode 100644 index ba460d2..0000000 --- a/.mops/base@0.11.1/src/Region.mo +++ /dev/null @@ -1,376 +0,0 @@ -/// Byte-level access to isolated, (virtual) stable memory _regions_. -/// -/// This is a moderately lightweight abstraction over IC _stable memory_ and supports persisting -/// regions of binary data across Motoko upgrades. -/// Use of this module is fully compatible with Motoko's use of -/// _stable variables_, whose persistence mechanism also uses (real) IC stable memory internally, but does not interfere with this API. -/// It is also fully compatible with existing uses of the `ExperimentalStableMemory` library, which has a similar interface, but, -/// only supported a single memory region, without isolation between different applications. -/// -/// Memory is allocated, using `grow(region, pages)`, sequentially and on demand, in units of 64KiB logical pages, starting with 0 allocated pages. -/// New pages are zero initialized. -/// Growth is capped by a soft limit on physical page count controlled by compile-time flag -/// `--max-stable-pages ` (the default is 65536, or 4GiB). -/// -/// Each `load` operation loads from region relative byte address `offset` in little-endian -/// format using the natural bit-width of the type in question. -/// The operation traps if attempting to read beyond the current region size. -/// -/// Each `store` operation stores to region relative byte address `offset` in little-endian format using the natural bit-width of the type in question. -/// The operation traps if attempting to write beyond the current region size. -/// -/// Text values can be handled by using `Text.decodeUtf8` and `Text.encodeUtf8`, in conjunction with `loadBlob` and `storeBlob`. -/// -/// The current region allocation and region contents are preserved across upgrades. -/// -/// NB: The IC's actual stable memory size (`ic0.stable_size`) may exceed the -/// total page size reported by summing all regions sizes. -/// This (and the cap on growth) are to accommodate Motoko's stable variables and bookkeeping for regions. -/// Applications that plan to use Motoko stable variables sparingly or not at all can -/// increase `--max-stable-pages` as desired, approaching the IC maximum (initially 8GiB, then 32Gib, currently 64Gib). -/// All applications should reserve at least one page for stable variable data, even when no stable variables are used. -/// -/// Usage: -/// ```motoko no-repl -/// import Region "mo:base/Region"; -/// ``` - -import Prim "mo:⛔"; - -module { - - /// A stateful handle to an isolated region of IC stable memory. - /// `Region` is a stable type and regions can be stored in stable variables. - public type Region = Prim.Types.Region; - - /// Allocate a new, isolated Region of size 0. - /// - /// Example: - /// - /// ```motoko no-repl - /// let region = Region.new(); - /// assert Region.size(region) == 0; - /// ``` - public let new : () -> Region = Prim.regionNew; - - /// Return a Nat identifying the given region. - /// Maybe be used for equality, comparison and hashing. - /// NB: Regions returned by `new()` are numbered from 16 - /// (regions 0..15 are currently reserved for internal use). - /// Allocate a new, isolated Region of size 0. - /// - /// Example: - /// - /// ```motoko no-repl - /// let region = Region.new(); - /// assert Region.id(region) == 16; - /// ``` - public let id : Region -> Nat = Prim.regionId; - - /// Current size of `region`, in pages. - /// Each page is 64KiB (65536 bytes). - /// Initially `0`. - /// Preserved across upgrades, together with contents of allocated - /// stable memory. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let beforeSize = Region.size(region); - /// ignore Region.grow(region, 10); - /// let afterSize = Region.size(region); - /// afterSize - beforeSize // => 10 - /// ``` - public let size : (region : Region) -> (pages : Nat64) = Prim.regionSize; - - /// Grow current `size` of `region` by the given number of pages. - /// Each page is 64KiB (65536 bytes). - /// Returns the previous `size` when able to grow. - /// Returns `0xFFFF_FFFF_FFFF_FFFF` if remaining pages insufficient. - /// Every new page is zero-initialized, containing byte 0x00 at every offset. - /// Function `grow` is capped by a soft limit on `size` controlled by compile-time flag - /// `--max-stable-pages ` (the default is 65536, or 4GiB). - /// - /// Example: - /// ```motoko no-repl - /// import Error "mo:base/Error"; - /// - /// let region = Region.new(); - /// let beforeSize = Region.grow(region, 10); - /// if (beforeSize == 0xFFFF_FFFF_FFFF_FFFF) { - /// throw Error.reject("Out of memory"); - /// }; - /// let afterSize = Region.size(region); - /// afterSize - beforeSize // => 10 - /// ``` - public let grow : (region : Region, newPages : Nat64) -> (oldPages : Nat64) = Prim.regionGrow; - - - /// Within `region`, load a `Nat8` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeNat8(region, offset, value); - /// Region.loadNat8(region, offset) // => 123 - /// ``` - public let loadNat8 : (region : Region, offset : Nat64) -> Nat8 = Prim.regionLoadNat8; - - /// Within `region`, store a `Nat8` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeNat8(region, offset, value); - /// Region.loadNat8(region, offset) // => 123 - /// ``` - public let storeNat8 : (region : Region, offset : Nat64, value : Nat8) -> () = Prim.regionStoreNat8; - - /// Within `region`, load a `Nat16` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeNat16(region, offset, value); - /// Region.loadNat16(region, offset) // => 123 - /// ``` - public let loadNat16 : (region : Region, offset : Nat64) -> Nat16 = Prim.regionLoadNat16; - - /// Within `region`, store a `Nat16` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeNat16(region, offset, value); - /// Region.loadNat16(region, offset) // => 123 - /// ``` - public let storeNat16 : (region : Region, offset : Nat64, value : Nat16) -> () = Prim.regionStoreNat16; - - /// Within `region`, load a `Nat32` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeNat32(region, offset, value); - /// Region.loadNat32(region, offset) // => 123 - /// ``` - public let loadNat32 : (region : Region, offset : Nat64) -> Nat32 = Prim.regionLoadNat32; - - /// Within `region`, store a `Nat32` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeNat32(region, offset, value); - /// Region.loadNat32(region, offset) // => 123 - /// ``` - public let storeNat32 : (region : Region, offset : Nat64, value : Nat32) -> () = Prim.regionStoreNat32; - - /// Within `region`, load a `Nat64` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeNat64(region, offset, value); - /// Region.loadNat64(region, offset) // => 123 - /// ``` - public let loadNat64 : (region : Region, offset : Nat64) -> Nat64 = Prim.regionLoadNat64; - - /// Within `region`, store a `Nat64` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeNat64(region, offset, value); - /// Region.loadNat64(region, offset) // => 123 - /// ``` - public let storeNat64 : (region : Region, offset : Nat64, value : Nat64) -> () = Prim.regionStoreNat64; - - /// Within `region`, load a `Int8` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeInt8(region, offset, value); - /// Region.loadInt8(region, offset) // => 123 - /// ``` - public let loadInt8 : (region : Region, offset : Nat64) -> Int8 = Prim.regionLoadInt8; - - /// Within `region`, store a `Int8` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeInt8(region, offset, value); - /// Region.loadInt8(region, offset) // => 123 - /// ``` - public let storeInt8 : (region : Region, offset : Nat64, value : Int8) -> () = Prim.regionStoreInt8; - - /// Within `region`, load a `Int16` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeInt16(region, offset, value); - /// Region.loadInt16(region, offset) // => 123 - /// ``` - public let loadInt16 : (region : Region, offset : Nat64) -> Int16 = Prim.regionLoadInt16; - - /// Within `region`, store a `Int16` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeInt16(region, offset, value); - /// Region.loadInt16(region, offset) // => 123 - /// ``` - public let storeInt16 : (region : Region, offset : Nat64, value : Int16) -> () = Prim.regionStoreInt16; - - /// Within `region`, load a `Int32` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeInt32(region, offset, value); - /// Region.loadInt32(region, offset) // => 123 - /// ``` - public let loadInt32 : (region : Region, offset : Nat64) -> Int32 = Prim.regionLoadInt32; - - /// Within `region`, store a `Int32` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeInt32(region, offset, value); - /// Region.loadInt32(region, offset) // => 123 - /// ``` - public let storeInt32 : (region : Region, offset : Nat64, value : Int32) -> () = Prim.regionStoreInt32; - - /// Within `region`, load a `Int64` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeInt64(region, offset, value); - /// Region.loadInt64(region, offset) // => 123 - /// ``` - public let loadInt64 : (region : Region, offset : Nat64) -> Int64 = Prim.regionLoadInt64; - - /// Within `region`, store a `Int64` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 123; - /// Region.storeInt64(region, offset, value); - /// Region.loadInt64(region, offset) // => 123 - /// ``` - public let storeInt64 : (region : Region, offset : Nat64, value : Int64) -> () = Prim.regionStoreInt64; - - - /// Within `region`, loads a `Float` value from the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 1.25; - /// Region.storeFloat(region, offset, value); - /// Region.loadFloat(region, offset) // => 1.25 - /// ``` - public let loadFloat : (region : Region, offset : Nat64) -> Float = Prim.regionLoadFloat; - - /// Within `region`, store float `value` at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// let region = Region.new(); - /// let offset = 0; - /// let value = 1.25; - /// Region.storeFloat(region, offset, value); - /// Region.loadFloat(region, offset) // => 1.25 - /// ``` - public let storeFloat : (region: Region, offset : Nat64, value : Float) -> () = Prim.regionStoreFloat; - - /// Within `region,` load `size` bytes starting from `offset` as a `Blob`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// import Blob "mo:base/Blob"; - /// - /// let region = Region.new(); - /// let offset = 0; - /// let value = Blob.fromArray([1, 2, 3]); - /// let size = value.size(); - /// Region.storeBlob(region, offset, value); - /// Blob.toArray(Region.loadBlob(region, offset, size)) // => [1, 2, 3] - /// ``` - public let loadBlob : (region : Region, offset : Nat64, size : Nat) -> Blob = Prim.regionLoadBlob; - - /// Within `region, write `blob.size()` bytes of `blob` beginning at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl - /// import Blob "mo:base/Blob"; - /// - /// let region = Region.new(); - /// let offset = 0; - /// let value = Blob.fromArray([1, 2, 3]); - /// let size = value.size(); - /// Region.storeBlob(region, offset, value); - /// Blob.toArray(Region.loadBlob(region, offset, size)) // => [1, 2, 3] - /// ``` - public let storeBlob : (region : Region, offset : Nat64, value : Blob) -> () = Prim.regionStoreBlob; - -} diff --git a/.mops/base@0.11.1/src/Result.mo b/.mops/base@0.11.1/src/Result.mo deleted file mode 100644 index 68ff6f0..0000000 --- a/.mops/base@0.11.1/src/Result.mo +++ /dev/null @@ -1,209 +0,0 @@ -/// Error handling with the Result type. - -import Prim "mo:⛔"; -import P "Prelude"; -import Order "Order"; - -module { - - /// `Result` is the type used for returning and propagating errors. It - /// is a type with the variants, `#ok(Ok)`, representing success and containing - /// a value, and `#err(Err)`, representing error and containing an error value. - /// - /// The simplest way of working with `Result`s is to pattern match on them: - /// - /// For example, given a function `createUser(user : User) : Result` - /// where `String` is an error message we could use it like so: - /// ```motoko no-repl - /// switch(createUser(myUser)) { - /// case (#ok(id)) { Debug.print("Created new user with id: " # id) }; - /// case (#err(msg)) { Debug.print("Failed to create user with the error: " # msg) }; - /// } - /// ``` - public type Result = { - #ok : Ok; - #err : Err - }; - - // Compares two Result's for equality. - public func equal( - eqOk : (Ok, Ok) -> Bool, - eqErr : (Err, Err) -> Bool, - r1 : Result, - r2 : Result - ) : Bool { - switch (r1, r2) { - case (#ok(ok1), #ok(ok2)) { - eqOk(ok1, ok2) - }; - case (#err(err1), #err(err2)) { - eqErr(err1, err2) - }; - case _ { false } - } - }; - - // Compares two Results. `#ok` is larger than `#err`. This ordering is - // arbitrary, but it lets you for example use Results as keys in ordered maps. - public func compare( - compareOk : (Ok, Ok) -> Order.Order, - compareErr : (Err, Err) -> Order.Order, - r1 : Result, - r2 : Result - ) : Order.Order { - switch (r1, r2) { - case (#ok(ok1), #ok(ok2)) { - compareOk(ok1, ok2) - }; - case (#err(err1), #err(err2)) { - compareErr(err1, err2) - }; - case (#ok(_), _) { #greater }; - case (#err(_), _) { #less } - } - }; - - /// Allows sequencing of `Result` values and functions that return - /// `Result`'s themselves. - /// ```motoko - /// import Result "mo:base/Result"; - /// type Result = Result.Result; - /// func largerThan10(x : Nat) : Result = - /// if (x > 10) { #ok(x) } else { #err("Not larger than 10.") }; - /// - /// func smallerThan20(x : Nat) : Result = - /// if (x < 20) { #ok(x) } else { #err("Not smaller than 20.") }; - /// - /// func between10And20(x : Nat) : Result = - /// Result.chain(largerThan10(x), smallerThan20); - /// - /// assert(between10And20(15) == #ok(15)); - /// assert(between10And20(9) == #err("Not larger than 10.")); - /// assert(between10And20(21) == #err("Not smaller than 20.")); - /// ``` - public func chain( - x : Result, - y : R1 -> Result - ) : Result { - switch x { - case (#err(e)) { #err(e) }; - case (#ok(r)) { y(r) } - } - }; - - /// Flattens a nested Result. - /// - /// ```motoko - /// import Result "mo:base/Result"; - /// assert(Result.flatten(#ok(#ok(10))) == #ok(10)); - /// assert(Result.flatten(#err("Wrong")) == #err("Wrong")); - /// assert(Result.flatten(#ok(#err("Wrong"))) == #err("Wrong")); - /// ``` - public func flatten( - result : Result, Error> - ) : Result { - switch result { - case (#ok(ok)) { ok }; - case (#err(err)) { #err(err) } - } - }; - - /// Maps the `Ok` type/value, leaving any `Error` type/value unchanged. - public func mapOk( - x : Result, - f : Ok1 -> Ok2 - ) : Result { - switch x { - case (#err(e)) { #err(e) }; - case (#ok(r)) { #ok(f(r)) } - } - }; - - /// Maps the `Err` type/value, leaving any `Ok` type/value unchanged. - public func mapErr( - x : Result, - f : Error1 -> Error2 - ) : Result { - switch x { - case (#err(e)) { #err(f(e)) }; - case (#ok(r)) { #ok(r) } - } - }; - - /// Create a result from an option, including an error value to handle the `null` case. - /// ```motoko - /// import Result "mo:base/Result"; - /// assert(Result.fromOption(?42, "err") == #ok(42)); - /// assert(Result.fromOption(null, "err") == #err("err")); - /// ``` - public func fromOption(x : ?R, err : E) : Result { - switch x { - case (?x) { #ok(x) }; - case null { #err(err) } - } - }; - - /// Create an option from a result, turning all #err into `null`. - /// ```motoko - /// import Result "mo:base/Result"; - /// assert(Result.toOption(#ok(42)) == ?42); - /// assert(Result.toOption(#err("err")) == null); - /// ``` - public func toOption(r : Result) : ?R { - switch r { - case (#ok(x)) { ?x }; - case (#err(_)) { null } - } - }; - - /// Applies a function to a successful value, but discards the result. Use - /// `iterate` if you're only interested in the side effect `f` produces. - /// - /// ```motoko - /// import Result "mo:base/Result"; - /// var counter : Nat = 0; - /// Result.iterate(#ok(5), func (x : Nat) { counter += x }); - /// assert(counter == 5); - /// Result.iterate(#err("Wrong"), func (x : Nat) { counter += x }); - /// assert(counter == 5); - /// ``` - public func iterate(res : Result, f : Ok -> ()) { - switch res { - case (#ok(ok)) { f(ok) }; - case _ {} - } - }; - - // Whether this Result is an `#ok` - public func isOk(r : Result) : Bool { - switch r { - case (#ok(_)) { true }; - case (#err(_)) { false } - } - }; - - // Whether this Result is an `#err` - public func isErr(r : Result) : Bool { - switch r { - case (#ok(_)) { false }; - case (#err(_)) { true } - } - }; - - /// Asserts that its argument is an `#ok` result, traps otherwise. - public func assertOk(r : Result) { - switch (r) { - case (#err(_)) { assert false }; - case (#ok(_)) {} - } - }; - - /// Asserts that its argument is an `#err` result, traps otherwise. - public func assertErr(r : Result) { - switch (r) { - case (#err(_)) {}; - case (#ok(_)) assert false - } - }; - -} diff --git a/.mops/base@0.11.1/src/Stack.mo b/.mops/base@0.11.1/src/Stack.mo deleted file mode 100644 index 7eadf21..0000000 --- a/.mops/base@0.11.1/src/Stack.mo +++ /dev/null @@ -1,93 +0,0 @@ -/// Class `Stack` provides a Minimal LIFO stack of elements of type `X`. -/// -/// See library `Deque` for mixed LIFO/FIFO behavior. -/// -/// Example: -/// ```motoko name=initialize -/// import Stack "mo:base/Stack"; -/// -/// let stack = Stack.Stack(); // create a stack -/// ``` -/// Runtime: O(1) -/// -/// Space: O(1) - -import List "List"; - -module { - - public class Stack() { - - var stack : List.List = List.nil(); - - /// Push an element on the top of the stack. - /// - /// Example: - /// ```motoko include=initialize - /// stack.push(1); - /// stack.push(2); - /// stack.push(3); - /// stack.peek(); // examine the top most element - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func push(x : T) { - stack := ?(x, stack) - }; - - /// True when the stack is empty and false otherwise. - /// - /// Example: - /// ```motoko include=initialize - /// stack.isEmpty(); - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func isEmpty() : Bool { - List.isNil(stack) - }; - - /// Return (without removing) the top element, or return null if the stack is empty. - /// - /// Example: - /// ```motoko include=initialize - /// stack.push(1); - /// stack.push(2); - /// stack.push(3); - /// stack.peek(); - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func peek() : ?T { - switch stack { - case null { null }; - case (?(h, t)) { ?h } - } - }; - - /// Remove and return the top element, or return null if the stack is empty. - /// - /// Example: - /// ```motoko include=initialize - /// stack.push(1); - /// ignore stack.pop(); - /// stack.isEmpty(); - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func pop() : ?T { - switch stack { - case null { null }; - case (?(h, t)) { stack := t; ?h } - } - } - } -} diff --git a/.mops/base@0.11.1/src/Text.mo b/.mops/base@0.11.1/src/Text.mo deleted file mode 100644 index 5af2f6e..0000000 --- a/.mops/base@0.11.1/src/Text.mo +++ /dev/null @@ -1,826 +0,0 @@ -/// Utility functions for `Text` values. -/// -/// A `Text` value represents human-readable text as a sequence of characters of type `Char`. -/// -/// ```motoko -/// let text = "Hello!"; -/// let size = text.size(); // 6 -/// let iter = text.chars(); // iterator ('H', 'e', 'l', 'l', 'o', '!') -/// let concat = text # " 👋"; // "Hello! 👋" -/// ``` -/// -/// The `"mo:base/Text"` module defines additional operations on `Text` values. -/// -/// Import the module from the base library: -/// -/// ```motoko name=import -/// import Text "mo:base/Text"; -/// ``` -/// -/// Note: `Text` values are represented as ropes of UTF-8 character sequences with O(1) concatenation. -/// - -import Char "Char"; -import Iter "Iter"; -import Hash "Hash"; -import Stack "Stack"; -import Prim "mo:⛔"; - -module { - - /// The type corresponding to primitive `Text` values. - /// - /// ```motoko - /// let hello = "Hello!"; - /// let emoji = "👋"; - /// let concat = hello # " " # emoji; // "Hello! 👋" - /// ``` - public type Text = Prim.Types.Text; - - /// Converts the given `Char` to a `Text` value. - /// - /// ```motoko include=import - /// let text = Text.fromChar('A'); // "A" - /// ``` - public let fromChar : (c : Char) -> Text = Prim.charToText; - - /// Iterates over each `Char` value in the given `Text`. - /// - /// Equivalent to calling the `t.chars()` method where `t` is a `Text` value. - /// - /// ```motoko include=import - /// import { print } "mo:base/Debug"; - /// - /// for (c in Text.toIter("abc")) { - /// print(debug_show c); - /// } - /// ``` - public func toIter(t : Text) : Iter.Iter = t.chars(); - - /// Creates a new `Array` containing characters of the given `Text`. - /// - /// Equivalent to `Iter.toArray(t.chars())`. - /// - /// ```motoko include=import - /// assert Text.toArray("Café") == ['C', 'a', 'f', 'é']; - /// ``` - /// - /// Runtime: O(t.size()) - /// Space: O(t.size()) - public func toArray(t : Text) : [Char] { - let cs = t.chars(); - // We rely on Array_tabulate's implementation details: it fills - // the array from left to right sequentially. - Prim.Array_tabulate( - t.size(), - func _ { - switch (cs.next()) { - case (?c) { c }; - case (null) { Prim.trap("Text.toArray") }; - }; - } - ) - }; - - /// Creates a new mutable `Array` containing characters of the given `Text`. - /// - /// Equivalent to `Iter.toArrayMut(t.chars())`. - /// - /// ```motoko include=import - /// assert Text.toVarArray("Café") == [var 'C', 'a', 'f', 'é']; - /// ``` - /// - /// Runtime: O(t.size()) - /// Space: O(t.size()) - public func toVarArray(t : Text) : [var Char] { - let n = t.size(); - if (n == 0) { - return [var]; - }; - let array = Prim.Array_init(n, ' '); - var i = 0; - for (c in t.chars()) { - array[i] := c; - i += 1; - }; - array - }; - - /// Creates a `Text` value from a `Char` iterator. - /// - /// ```motoko include=import - /// let text = Text.fromIter(['a', 'b', 'c'].vals()); // "abc" - /// ``` - public func fromIter(cs : Iter.Iter) : Text { - var r = ""; - for (c in cs) { - r #= Prim.charToText(c) - }; - return r - }; - - /// Returns the number of characters in the given `Text`. - /// - /// Equivalent to calling `t.size()` where `t` is a `Text` value. - /// - /// ```motoko include=import - /// let size = Text.size("abc"); // 3 - /// ``` - public func size(t : Text) : Nat { t.size() }; - - /// Returns a hash obtained by using the `djb2` algorithm ([more details](http://www.cse.yorku.ca/~oz/hash.html)). - /// - /// ```motoko include=import - /// let hash = Text.hash("abc"); - /// ``` - /// - /// Note: this algorithm is intended for use in data structures rather than as a cryptographic hash function. - public func hash(t : Text) : Hash.Hash { - var x : Nat32 = 5381; - for (char in t.chars()) { - let c : Nat32 = Prim.charToNat32(char); - x := ((x << 5) +% x) +% c - }; - return x - }; - - /// Returns `t1 # t2`, where `#` is the `Text` concatenation operator. - /// - /// ```motoko include=import - /// let a = "Hello"; - /// let b = "There"; - /// let together = a # b; // "HelloThere" - /// let withSpace = a # " " # b; // "Hello There" - /// let togetherAgain = Text.concat(a, b); // "HelloThere" - /// ``` - public func concat(t1 : Text, t2 : Text) : Text = t1 # t2; - - /// Returns `t1 == t2`. - public func equal(t1 : Text, t2 : Text) : Bool { t1 == t2 }; - - /// Returns `t1 != t2`. - public func notEqual(t1 : Text, t2 : Text) : Bool { t1 != t2 }; - - /// Returns `t1 < t2`. - public func less(t1 : Text, t2 : Text) : Bool { t1 < t2 }; - - /// Returns `t1 <= t2`. - public func lessOrEqual(t1 : Text, t2 : Text) : Bool { t1 <= t2 }; - - /// Returns `t1 > t2`. - public func greater(t1 : Text, t2 : Text) : Bool { t1 > t2 }; - - /// Returns `t1 >= t2`. - public func greaterOrEqual(t1 : Text, t2 : Text) : Bool { t1 >= t2 }; - - /// Compares `t1` and `t2` lexicographically. - /// - /// ```motoko include=import - /// import { print } "mo:base/Debug"; - /// - /// print(debug_show Text.compare("abc", "abc")); // #equal - /// print(debug_show Text.compare("abc", "def")); // #less - /// print(debug_show Text.compare("abc", "ABC")); // #greater - /// ``` - public func compare(t1 : Text, t2 : Text) : { #less; #equal; #greater } { - let c = Prim.textCompare(t1, t2); - if (c < 0) #less else if (c == 0) #equal else #greater - }; - - private func extract(t : Text, i : Nat, j : Nat) : Text { - let size = t.size(); - if (i == 0 and j == size) return t; - assert (j <= size); - let cs = t.chars(); - var r = ""; - var n = i; - while (n > 0) { - ignore cs.next(); - n -= 1 - }; - n := j; - while (n > 0) { - switch (cs.next()) { - case null { assert false }; - case (?c) { r #= Prim.charToText(c) } - }; - n -= 1 - }; - return r - }; - - /// Join an iterator of `Text` values with a given delimiter. - /// - /// ```motoko include=import - /// let joined = Text.join(", ", ["a", "b", "c"].vals()); // "a, b, c" - /// ``` - public func join(sep : Text, ts : Iter.Iter) : Text { - var r = ""; - if (sep.size() == 0) { - for (t in ts) { - r #= t - }; - return r - }; - let next = ts.next; - switch (next()) { - case null { return r }; - case (?t) { - r #= t - } - }; - loop { - switch (next()) { - case null { return r }; - case (?t) { - r #= sep; - r #= t - } - } - } - }; - - /// Applies a function to each character in a `Text` value, returning the concatenated `Char` results. - /// - /// ```motoko include=import - /// // Replace all occurrences of '?' with '!' - /// let result = Text.map("Motoko?", func(c) { - /// if (c == '?') '!' - /// else c - /// }); - /// ``` - public func map(t : Text, f : Char -> Char) : Text { - var r = ""; - for (c in t.chars()) { - r #= Prim.charToText(f(c)) - }; - return r - }; - - /// Returns the result of applying `f` to each character in `ts`, concatenating the intermediate text values. - /// - /// ```motoko include=import - /// // Replace all occurrences of '?' with "!!" - /// let result = Text.translate("Motoko?", func(c) { - /// if (c == '?') "!!" - /// else Text.fromChar(c) - /// }); // "Motoko!!" - /// ``` - public func translate(t : Text, f : Char -> Text) : Text { - var r = ""; - for (c in t.chars()) { - r #= f(c) - }; - return r - }; - - /// A pattern `p` describes a sequence of characters. A pattern has one of the following forms: - /// - /// * `#char c` matches the single character sequence, `c`. - /// * `#text t` matches multi-character text sequence `t`. - /// * `#predicate p` matches any single character sequence `c` satisfying predicate `p(c)`. - /// - /// A _match_ for `p` is any sequence of characters matching the pattern `p`. - /// - /// ```motoko include=import - /// let charPattern = #char 'A'; - /// let textPattern = #text "phrase"; - /// let predicatePattern : Text.Pattern = #predicate (func(c) { c == 'A' or c == 'B' }); // matches "A" or "B" - /// ``` - public type Pattern = { - #char : Char; - #text : Text; - #predicate : (Char -> Bool) - }; - - private func take(n : Nat, cs : Iter.Iter) : Iter.Iter { - var i = n; - object { - public func next() : ?Char { - if (i == 0) return null; - i -= 1; - return cs.next() - } - } - }; - - private func empty() : Iter.Iter { - object { - public func next() : ?Char = null - } - }; - - private type Match = { - /// #success on complete match - #success; - /// #fail(cs,c) on partial match of cs, but failing match on c - #fail : (cs : Iter.Iter, c : Char); - /// #empty(cs) on partial match of cs and empty stream - #empty : (cs : Iter.Iter) - }; - - private func sizeOfPattern(pat : Pattern) : Nat { - switch pat { - case (#text(t)) { t.size() }; - case (#predicate(_) or #char(_)) { 1 } - } - }; - - private func matchOfPattern(pat : Pattern) : (cs : Iter.Iter) -> Match { - switch pat { - case (#char(p)) { - func(cs : Iter.Iter) : Match { - switch (cs.next()) { - case (?c) { - if (p == c) { - #success - } else { - #fail(empty(), c) - } - }; - case null { #empty(empty()) } - } - } - }; - case (#predicate(p)) { - func(cs : Iter.Iter) : Match { - switch (cs.next()) { - case (?c) { - if (p(c)) { - #success - } else { - #fail(empty(), c) - } - }; - case null { #empty(empty()) } - } - } - }; - case (#text(p)) { - func(cs : Iter.Iter) : Match { - var i = 0; - let ds = p.chars(); - loop { - switch (ds.next()) { - case (?d) { - switch (cs.next()) { - case (?c) { - if (c != d) { - return #fail(take(i, p.chars()), c) - }; - i += 1 - }; - case null { - return #empty(take(i, p.chars())) - } - } - }; - case null { return #success } - } - } - } - } - } - }; - - private class CharBuffer(cs : Iter.Iter) : Iter.Iter = { - - var stack : Stack.Stack<(Iter.Iter, Char)> = Stack.Stack(); - - public func pushBack(cs0 : Iter.Iter, c : Char) { - stack.push((cs0, c)) - }; - - public func next() : ?Char { - switch (stack.peek()) { - case (?(buff, c)) { - switch (buff.next()) { - case null { - ignore stack.pop(); - return ?c - }; - case oc { - return oc - } - } - }; - case null { - return cs.next() - } - } - } - }; - - /// Splits the input `Text` with the specified `Pattern`. - /// - /// Two fields are separated by exactly one match. - /// - /// ```motoko include=import - /// let words = Text.split("This is a sentence.", #char ' '); - /// Text.join("|", words) // "This|is|a|sentence." - /// ``` - public func split(t : Text, p : Pattern) : Iter.Iter { - let match = matchOfPattern(p); - let cs = CharBuffer(t.chars()); - var state = 0; - var field = ""; - object { - public func next() : ?Text { - switch state { - case (0 or 1) { - loop { - switch (match(cs)) { - case (#success) { - let r = field; - field := ""; - state := 1; - return ?r - }; - case (#empty(cs1)) { - for (c in cs1) { - field #= fromChar(c) - }; - let r = if (state == 0 and field == "") { - null - } else { - ?field - }; - state := 2; - return r - }; - case (#fail(cs1, c)) { - cs.pushBack(cs1, c); - switch (cs.next()) { - case (?ci) { - field #= fromChar(ci) - }; - case null { - let r = if (state == 0 and field == "") { - null - } else { - ?field - }; - state := 2; - return r - } - } - } - } - } - }; - case _ { return null } - } - } - } - }; - - /// Returns a sequence of tokens from the input `Text` delimited by the specified `Pattern`, derived from start to end. - /// A "token" is a non-empty maximal subsequence of `t` not containing a match for pattern `p`. - /// Two tokens may be separated by one or more matches of `p`. - /// - /// ```motoko include=import - /// let tokens = Text.tokens("this needs\n an example", #predicate (func(c) { c == ' ' or c == '\n' })); - /// Text.join("|", tokens) // "this|needs|an|example" - /// ``` - public func tokens(t : Text, p : Pattern) : Iter.Iter { - let fs = split(t, p); - object { - public func next() : ?Text { - switch (fs.next()) { - case (?"") { next() }; - case ot { ot } - } - } - } - }; - - /// Returns `true` if the input `Text` contains a match for the specified `Pattern`. - /// - /// ```motoko include=import - /// Text.contains("Motoko", #text "oto") // true - /// ``` - public func contains(t : Text, p : Pattern) : Bool { - let match = matchOfPattern(p); - let cs = CharBuffer(t.chars()); - loop { - switch (match(cs)) { - case (#success) { - return true - }; - case (#empty(cs1)) { - return false - }; - case (#fail(cs1, c)) { - cs.pushBack(cs1, c); - switch (cs.next()) { - case null { - return false - }; - case _ {}; // continue - } - } - } - } - }; - - /// Returns `true` if the input `Text` starts with a prefix matching the specified `Pattern`. - /// - /// ```motoko include=import - /// Text.startsWith("Motoko", #text "Mo") // true - /// ``` - public func startsWith(t : Text, p : Pattern) : Bool { - var cs = t.chars(); - let match = matchOfPattern(p); - switch (match(cs)) { - case (#success) { true }; - case _ { false } - } - }; - - /// Returns `true` if the input `Text` ends with a suffix matching the specified `Pattern`. - /// - /// ```motoko include=import - /// Text.endsWith("Motoko", #char 'o') // true - /// ``` - public func endsWith(t : Text, p : Pattern) : Bool { - let s2 = sizeOfPattern(p); - if (s2 == 0) return true; - let s1 = t.size(); - if (s2 > s1) return false; - let match = matchOfPattern(p); - var cs1 = t.chars(); - var diff : Nat = s1 - s2; - while (diff > 0) { - ignore cs1.next(); - diff -= 1 - }; - switch (match(cs1)) { - case (#success) { true }; - case _ { false } - } - }; - - /// Returns the input text `t` with all matches of pattern `p` replaced by text `r`. - /// - /// ```motoko include=import - /// let result = Text.replace("abcabc", #char 'a', "A"); // "AbcAbc" - /// ``` - public func replace(t : Text, p : Pattern, r : Text) : Text { - let match = matchOfPattern(p); - let size = sizeOfPattern(p); - let cs = CharBuffer(t.chars()); - var res = ""; - label l loop { - switch (match(cs)) { - case (#success) { - res #= r; - if (size > 0) { - continue l - } - }; - case (#empty(cs1)) { - for (c1 in cs1) { - res #= fromChar(c1) - }; - break l - }; - case (#fail(cs1, c)) { - cs.pushBack(cs1, c) - } - }; - switch (cs.next()) { - case null { - break l - }; - case (?c1) { - res #= fromChar(c1) - }; // continue - } - }; - return res - }; - - /// Strips one occurrence of the given `Pattern` from the beginning of the input `Text`. - /// If you want to remove multiple instances of the pattern, use `Text.trimStart()` instead. - /// - /// ```motoko include=import - /// // Try to strip a nonexistent character - /// let none = Text.stripStart("abc", #char '-'); // null - /// // Strip just one '-' - /// let one = Text.stripStart("--abc", #char '-'); // ?"-abc" - /// ``` - public func stripStart(t : Text, p : Pattern) : ?Text { - let s = sizeOfPattern(p); - if (s == 0) return ?t; - var cs = t.chars(); - let match = matchOfPattern(p); - switch (match(cs)) { - case (#success) return ?fromIter(cs); - case _ return null - } - }; - - /// Strips one occurrence of the given `Pattern` from the end of the input `Text`. - /// If you want to remove multiple instances of the pattern, use `Text.trimEnd()` instead. - /// - /// ```motoko include=import - /// // Try to strip a nonexistent character - /// let none = Text.stripEnd("xyz", #char '-'); // null - /// // Strip just one '-' - /// let one = Text.stripEnd("xyz--", #char '-'); // ?"xyz-" - /// ``` - public func stripEnd(t : Text, p : Pattern) : ?Text { - let s2 = sizeOfPattern(p); - if (s2 == 0) return ?t; - let s1 = t.size(); - if (s2 > s1) return null; - let match = matchOfPattern(p); - var cs1 = t.chars(); - var diff : Nat = s1 - s2; - while (diff > 0) { - ignore cs1.next(); - diff -= 1 - }; - switch (match(cs1)) { - case (#success) return ?extract(t, 0, s1 - s2); - case _ return null - } - }; - - /// Trims the given `Pattern` from the start of the input `Text`. - /// If you only want to remove a single instance of the pattern, use `Text.stripStart()` instead. - /// - /// ```motoko include=import - /// let trimmed = Text.trimStart("---abc", #char '-'); // "abc" - /// ``` - public func trimStart(t : Text, p : Pattern) : Text { - let cs = t.chars(); - let size = sizeOfPattern(p); - if (size == 0) return t; - var matchSize = 0; - let match = matchOfPattern(p); - loop { - switch (match(cs)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(cs1)) { - return if (matchSize == 0) { - t - } else { - fromIter(cs1) - } - }; - case (#fail(cs1, c)) { - return if (matchSize == 0) { - t - } else { - fromIter(cs1) # fromChar(c) # fromIter(cs) - } - } - } - } - }; - - /// Trims the given `Pattern` from the end of the input `Text`. - /// If you only want to remove a single instance of the pattern, use `Text.stripEnd()` instead. - /// - /// ```motoko include=import - /// let trimmed = Text.trimEnd("xyz---", #char '-'); // "xyz" - /// ``` - public func trimEnd(t : Text, p : Pattern) : Text { - let cs = CharBuffer(t.chars()); - let size = sizeOfPattern(p); - if (size == 0) return t; - let match = matchOfPattern(p); - var matchSize = 0; - label l loop { - switch (match(cs)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(cs1)) { - switch (cs1.next()) { - case null break l; - case (?_) return t - } - }; - case (#fail(cs1, c)) { - matchSize := 0; - cs.pushBack(cs1, c); - ignore cs.next() - } - } - }; - extract(t, 0, t.size() - matchSize) - }; - - /// Trims the given `Pattern` from both the start and end of the input `Text`. - /// - /// ```motoko include=import - /// let trimmed = Text.trim("---abcxyz---", #char '-'); // "abcxyz" - /// ``` - public func trim(t : Text, p : Pattern) : Text { - let cs = t.chars(); - let size = sizeOfPattern(p); - if (size == 0) return t; - var matchSize = 0; - let match = matchOfPattern(p); - loop { - switch (match(cs)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(cs1)) { - return if (matchSize == 0) { t } else { fromIter(cs1) } - }; - case (#fail(cs1, c)) { - let start = matchSize; - let cs2 = CharBuffer(cs); - cs2.pushBack(cs1, c); - ignore cs2.next(); - matchSize := 0; - label l loop { - switch (match(cs2)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(cs3)) { - switch (cs1.next()) { - case null break l; - case (?_) return t - } - }; - case (#fail(cs3, c1)) { - matchSize := 0; - cs2.pushBack(cs3, c1); - ignore cs2.next() - } - } - }; - return extract(t, start, t.size() - matchSize - start) - } - } - } - }; - - /// Compares `t1` and `t2` using the provided character-wise comparison function. - /// - /// ```motoko include=import - /// import Char "mo:base/Char"; - /// - /// Text.compareWith("abc", "ABC", func(c1, c2) { Char.compare(c1, c2) }) // #greater - /// ``` - public func compareWith( - t1 : Text, - t2 : Text, - cmp : (Char, Char) -> { #less; #equal; #greater } - ) : { #less; #equal; #greater } { - let cs1 = t1.chars(); - let cs2 = t2.chars(); - loop { - switch (cs1.next(), cs2.next()) { - case (null, null) { return #equal }; - case (null, ?_) { return #less }; - case (?_, null) { return #greater }; - case (?c1, ?c2) { - switch (cmp(c1, c2)) { - case (#equal) {}; // continue - case other { return other } - } - } - } - } - }; - - /// Returns a UTF-8 encoded `Blob` from the given `Text`. - /// - /// ```motoko include=import - /// let blob = Text.encodeUtf8("Hello"); - /// ``` - public let encodeUtf8 : Text -> Blob = Prim.encodeUtf8; - - /// Tries to decode the given `Blob` as UTF-8. - /// Returns `null` if the blob is not valid UTF-8. - /// - /// ```motoko include=import - /// let text = Text.decodeUtf8("\48\65\6C\6C\6F"); // ?"Hello" - /// ``` - public let decodeUtf8 : Blob -> ?Text = Prim.decodeUtf8; - - /// Returns the text argument in lowercase. - /// WARNING: Unicode compliant only when compiled, not interpreted. - /// - /// ```motoko include=import - /// let text = Text.toLowercase("Good Day"); // ?"good day" - /// ``` - public let toLowercase : Text -> Text = Prim.textLowercase; - - /// Returns the text argument in uppercase. Unicode compliant. - /// WARNING: Unicode compliant only when compiled, not interpreted. - /// - /// ```motoko include=import - /// let text = Text.toUppercase("Good Day"); // ?"GOOD DAY" - /// ``` - public let toUppercase : Text -> Text = Prim.textUppercase; -} diff --git a/.mops/base@0.11.1/src/Time.mo b/.mops/base@0.11.1/src/Time.mo deleted file mode 100644 index 940ff30..0000000 --- a/.mops/base@0.11.1/src/Time.mo +++ /dev/null @@ -1,36 +0,0 @@ -/// System time - -import Prim "mo:⛔"; -module { - - /// System time is represent as nanoseconds since 1970-01-01. - public type Time = Int; - - /// Current system time given as nanoseconds since 1970-01-01. The system guarantees that: - /// - /// * the time, as observed by the canister smart contract, is monotonically increasing, even across canister upgrades. - /// * within an invocation of one entry point, the time is constant. - /// - /// The system times of different canisters are unrelated, and calls from one canister to another may appear to travel "backwards in time" - /// - /// Note: While an implementation will likely try to keep the system time close to the real time, this is not formally guaranteed. - public let now : () -> Time = func() : Int = Prim.nat64ToNat(Prim.time()); - /// - /// The following example illustrates using the system time: - /// - /// ```motoko - /// import Int = "mo:base/Int"; - /// import Time = "mo:base/Time"; - /// - /// actor { - /// var lastTime = Time.now(); - /// public func greet(name : Text) : async Text { - /// let now = Time.now(); - /// let elapsedSeconds = (now - lastTime) / 1000_000_000; - /// lastTime := now; - /// return "Hello, " # name # "!" # - /// " I was last called " # Int.toText(elapsedSeconds) # " seconds ago"; - /// }; - /// }; - /// ``` -} diff --git a/.mops/base@0.11.1/src/Timer.mo b/.mops/base@0.11.1/src/Timer.mo deleted file mode 100644 index f1a322f..0000000 --- a/.mops/base@0.11.1/src/Timer.mo +++ /dev/null @@ -1,62 +0,0 @@ -/// Timers for one-off or periodic tasks. -/// -/// Note: If `moc` is invoked with `-no-timer`, the importing will fail. -/// Note: The resolution of the timers is in the order of the block rate, -/// so durations should be chosen well above that. For frequent -/// canister wake-ups the heatbeat mechanism should be considered. - -import { setTimer = setTimerNano; cancelTimer = cancel } = "mo:⛔"; -import { fromIntWrap } = "Nat64"; - -module { - - public type Duration = { #seconds : Nat; #nanoseconds : Nat }; - public type TimerId = Nat; - - func toNanos(d : Duration) : Nat64 = - fromIntWrap (switch d { - case (#seconds s) s * 1000_000_000; - case (#nanoseconds ns) ns }); - - /// Installs a one-off timer that upon expiration after given duration `d` - /// executes the future `job()`. - /// - /// ```motoko no-repl - /// let now = Time.now(); - /// let thirtyMinutes = 1_000_000_000 * 60 * 30; - /// func alarmUser() : async () { - /// // ... - /// }; - /// appt.reminder = setTimer(#nanoseconds (Int.abs(appt.when - now - thirtyMinutes)), alarmUser); - /// ``` - public func setTimer(d : Duration, job : () -> async ()) : TimerId { - setTimerNano(toNanos d, false, job) - }; - - /// Installs a recurring timer that upon expiration after given duration `d` - /// executes the future `job()` and reinserts itself for another expiration. - /// - /// Note: A duration of 0 will only expire once. - /// - /// ```motoko no-repl - /// func checkAndWaterPlants() : async () { - /// // ... - /// }; - /// let daily = recurringTimer(#seconds (24 * 60 * 60), checkAndWaterPlants); - /// ``` - public func recurringTimer(d : Duration, job : () -> async ()) : TimerId { - setTimerNano(toNanos d, true, job) - }; - - /// Cancels a still active timer with `(id : TimerId)`. For expired timers - /// and not recognised `id`s nothing happens. - /// - /// ```motoko no-repl - /// func deleteAppt(appt : Appointment) { - /// cancelTimer (appt.reminder); - /// // ... - /// }; - /// ``` - public let cancelTimer : TimerId -> () = cancel; - -} diff --git a/.mops/base@0.11.1/src/Trie.mo b/.mops/base@0.11.1/src/Trie.mo deleted file mode 100644 index d457cbf..0000000 --- a/.mops/base@0.11.1/src/Trie.mo +++ /dev/null @@ -1,1576 +0,0 @@ -/// Functional key-value hash maps. -/// -/// Functional maps (and sets) whose representation is "canonical", and -/// independent of operation history (unlike other popular search trees). -/// -/// The representation we use here comes from Section 6 of ["Incremental computation via function caching", Pugh & Teitelbaum](https://dl.acm.org/citation.cfm?id=75305). -/// -/// ## User's overview -/// -/// This module provides an applicative (functional) hash map. -/// Notably, each `put` produces a **new trie _and value being replaced, if any_**. -/// -/// Those looking for a more familiar (imperative, -/// object-oriented) hash map should consider `TrieMap` or `HashMap` instead. -/// -/// The basic `Trie` operations consist of: -/// - `put` - put a key-value into the trie, producing a new version. -/// - `get` - get a key's value from the trie, or `null` if none. -/// - `remove` - remove a key's value from the trie -/// - `iter` - visit every key-value in the trie. -/// -/// The `put`, `get` and `remove` operations work over `Key` records, -/// which group the hash of the key with its non-hash key value. -/// -/// Example: -/// ```motoko -/// import Trie "mo:base/Trie"; -/// import Text "mo:base/Text"; -/// -/// // we do this to have shorter type names and thus -/// // better readibility -/// type Trie = Trie.Trie; -/// type Key = Trie.Key; -/// -/// // we have to provide `put`, `get` and `remove` with -/// // a record of type `Key = { hash : Hash.Hash; key : K }`; -/// // thus we define the following function that takes a value of type `K` -/// // (in this case `Text`) and returns a `Key` record. -/// func key(t: Text) : Key { { hash = Text.hash t; key = t } }; -/// -/// // we start off by creating an empty `Trie` -/// let t0 : Trie = Trie.empty(); -/// -/// // `put` requires 4 arguments: -/// // - the trie we want to insert the value into, -/// // - the key of the value we want to insert (note that we use the `key` function defined above), -/// // - a function that checks for equality of keys, and -/// // - the value we want to insert. -/// // -/// // When inserting a value, `put` returns a tuple of type `(Trie, ?V)`. -/// // to get the new trie that contains the value, we use the `0` projection -/// // and assign it to `t1` and `t2` respectively. -/// let t1 : Trie = Trie.put(t0, key "hello", Text.equal, 42).0; -/// let t2 : Trie = Trie.put(t1, key "world", Text.equal, 24).0; -/// -/// // If for a given key there already was a value in the trie, `put` returns -/// // that previous value as the second element of the tuple. -/// // in our case we have already inserted the value 42 for the key "hello", so -/// // `put` returns 42 as the second element of the tuple. -/// let (t3, n) : (Trie, ?Nat) = Trie.put( -/// t2, -/// key "hello", -/// Text.equal, -/// 0, -/// ); -/// assert (n == ?42); -/// -/// // `get` requires 3 arguments: -/// // - the trie we want to get the value from -/// // - the key of the value we want to get (note that we use the `key` function defined above) -/// // - a function that checks for equality of keys -/// // -/// // If the given key is nonexistent in the trie, `get` returns `null`. -/// var value = Trie.get(t3, key "hello", Text.equal); // Returns `?42` -/// assert(value == ?0); -/// value := Trie.get(t3, key "universe", Text.equal); // Returns `null` -/// assert(value == null); -/// -/// // `remove` requires 3 arguments: -/// // - the trie we want to remove the value from, -/// // - the key of the value we want to remove (note that we use the `key` function defined above), and -/// // - a function that checks for equality of keys. -/// // -/// // In the case of keys of type `Text`, we can use `Text.equal` -/// // to check for equality of keys. Function `remove` returns a tuple of type `(Trie, ?V)`. -/// // where the second element of the tuple is the value that was removed, or `null` if -/// // there was no value for the given key. -/// let removedValue : ?Nat = Trie.remove( -/// t3, -/// key "hello", -/// Text.equal, -/// ).1; -/// assert (removedValue == ?0); -/// -/// // To iterate over the Trie, we use the `iter` function that takes a trie -/// // of type `Trie` and returns an iterator of type `Iter<(K,V)>`: -/// var sum : Nat = 0; -/// for (kv in Trie.iter(t3)) { -/// sum += kv.1; -/// }; -/// assert(sum == 24); -/// ``` - -// ## Implementation overview -// -// A (hash) trie is a binary tree container for key-value pairs that -// consists of leaf and branch nodes. -// -// Each internal **branch node** -// represents having distinguished its key-value pairs on a single bit of -// the keys. -// By following paths in the trie, we determine an increasingly smaller -// and smaller subset of the keys. -// -// Each **leaf node** consists of an association list of key-value pairs. -// -// Each non-empty trie node stores a size; we discuss that more below. -// -// ### Adaptive depth -// -// We say that a leaf is valid if it contains no more than `MAX_LEAF_SIZE` -// key-value pairs. When a leaf node grows too large, the -// binary tree produces a new internal binary node, and splits the leaf into -// a pair of leaves using an additional bit of their keys' hash strings. -// -// For small mappings, the trie structure consists of a single -// leaf, which contains up to MAX_LEAF_SIZE key-value pairs. -// -// ### Cached sizes -// -// At each branch and leaf, we use a stored size to support a -// memory-efficient `toArray` function, which itself relies on -// per-element projection via `nth`; in turn, `nth` directly uses the -// O(1)-time function `size` for achieving an acceptable level of -// algorithmic efficiency. Notably, leaves are generally lists of -// key-value pairs, and we do not store a size for each Cons cell in the -// list. -// - -import Debug "Debug"; - -import Prim "mo:⛔"; -import P "Prelude"; -import Option "Option"; -import Hash "Hash"; -import A "Array"; - -import List "List"; -import AssocList "AssocList"; -import I "Iter"; - -module { - - let MAX_LEAF_SIZE = 8; // to do -- further profiling and tuning - - /// Binary hash tries: either empty, a leaf node, or a branch node - public type Trie = { - #empty; - #leaf : Leaf; - #branch : Branch - }; - - /// Leaf nodes of trie consist of key-value pairs as a list. - public type Leaf = { - size : Nat; - keyvals : AssocList, V> - }; - - /// Branch nodes of the trie discriminate on a bit position of the keys' hashes. - /// we never store this bitpos; rather, - /// we enforce a style where this position is always known from context. - public type Branch = { - size : Nat; - left : Trie; - right : Trie - }; - - public type AssocList = AssocList.AssocList; - - /// A `Key` for the trie has an associated hash value - /// - `hash` permits fast inequality checks, and permits collisions, while - /// - `key` permits precise equality checks, but is only used on values with equal hashes. - public type Key = { - hash : Hash.Hash; - key : K - }; - - type List = List.List; - - /// Equality function for two `Key`s, in terms of equality of `K`'s. - public func equalKey(keq : (K, K) -> Bool) : ((Key, Key) -> Bool) = - func(key1 : Key, key2 : Key) : Bool = - Hash.equal(key1.hash, key2.hash) and keq(key1.key, key2.key); - - /// @deprecated `isValid` is an internal predicate and will be removed in future. - public func isValid(t : Trie, _enforceNormal : Bool) : Bool { - func rec(t : Trie, bitpos : ?Hash.Hash, bits : Hash.Hash, mask : Hash.Hash) : Bool = - switch t { - case (#empty) { - true - }; - case (#leaf l) { - let len = List.size(l.keyvals); - len <= MAX_LEAF_SIZE and len == l.size and List.all( - l.keyvals, - func((k : Key, _v : V)) : Bool { ((k.hash & mask) == bits) } - ) - }; - case (#branch b) { - let bitpos1 = switch bitpos { - case null { Prim.natToNat32(0) }; - case (?bp) { Prim.natToNat32(Prim.nat32ToNat(bp) + 1) } - }; - let mask1 = mask | (Prim.natToNat32(1) << bitpos1); - let bits1 = bits | (Prim.natToNat32(1) << bitpos1); - let sum = size(b.left) + size(b.right); - (b.size == sum) and rec(b.left, ?bitpos1, bits, mask1) and rec(b.right, ?bitpos1, bits1, mask1) - } - }; - rec(t, null, 0, 0) - }; - - /// A 2D trie maps dimension-1 keys to another - /// layer of tries, each keyed on the dimension-2 keys. - public type Trie2D = Trie>; - - /// A 3D trie maps dimension-1 keys to another - /// Composition of 2D tries, each keyed on the dimension-2 and dimension-3 keys. - public type Trie3D = Trie>; - - /// An empty trie. This is usually the starting point for building a trie. - /// - /// Example: - /// ```motoko name=initialize - /// import { print } "mo:base/Debug"; - /// import Trie "mo:base/Trie"; - /// import Text "mo:base/Text"; - /// - /// // we do this to have shorter type names and thus - /// // better readibility - /// type Trie = Trie.Trie; - /// type Key = Trie.Key; - /// - /// // We have to provide `put`, `get` and `remove` with - /// // a function of return type `Key = { hash : Hash.Hash; key : K }` - /// func key(t: Text) : Key { { hash = Text.hash t; key = t } }; - /// // We start off by creating an empty `Trie` - /// var trie : Trie = Trie.empty(); - /// ``` - public func empty() : Trie = #empty; - - /// Get the size in O(1) time. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// var size = Trie.size(trie); // Returns 0, as `trie` is empty - /// assert(size == 0); - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// size := Trie.size(trie); // Returns 1, as we just added a new entry - /// assert(size == 1); - /// ``` - - public func size(t : Trie) : Nat = - switch t { - case (#empty) { 0 }; - case (#leaf l) { l.size }; - case (#branch b) { b.size } - }; - - /// Construct a branch node, computing the size stored there. - public func branch(l : Trie, r : Trie) : Trie = - #branch { - size = size l + size r; - left = l; - right = r - }; - - /// Construct a leaf node, computing the size stored there. - /// - /// This helper function automatically enforces the MAX_LEAF_SIZE - /// by constructing branches as necessary; to do so, it also needs the bitpos - /// of the leaf. - public func leaf(kvs : AssocList, V>, bitpos : Nat) : Trie = - fromList(null, kvs, bitpos); - - module ListUtil { - /* Deprecated: List.lenClamp */ - /// Return the list length unless the number of items in the list exceeds - /// a maximum value. If the list length exceed the maximum, the function - /// returns `null`. - public func lenClamp(l : List, max : Nat) : ?Nat { - func rec(l : List, max : Nat, i : Nat) : ?Nat = - switch l { - case null { ?i }; - case (?(_, t)) { - if (i >= max) { null } else { rec(t, max, i + 1) } - } - }; - rec(l, max, 0) - } - }; - - /// Transform a list into a trie, splitting input list into small (leaf) lists, if necessary. - public func fromList(kvc : ?Nat, kvs : AssocList, V>, bitpos : Nat) : Trie { - func rec(kvc : ?Nat, kvs : AssocList, V>, bitpos : Nat) : Trie { - switch kvc { - case null { - switch (ListUtil.lenClamp(kvs, MAX_LEAF_SIZE)) { - case null {} /* fall through to branch case. */; - case (?len) { - return #leaf { size = len; keyvals = kvs } - } - } - }; - case (?c) { - if (c == 0) { - return #empty - } else if (c <= MAX_LEAF_SIZE) { - return #leaf { size = c; keyvals = kvs } - } else { - - //fall through to branch case - } - } - }; - let (ls, l, rs, r) = splitList(kvs, bitpos); - if (ls == 0 and rs == 0) { - #empty - } else if (rs == 0 and ls <= MAX_LEAF_SIZE) { - #leaf { size = ls; keyvals = l } - } else if (ls == 0 and rs <= MAX_LEAF_SIZE) { - #leaf { size = rs; keyvals = r } - } else { - branch(rec(?ls, l, bitpos + 1), rec(?rs, r, bitpos + 1)) - } - }; - rec(kvc, kvs, bitpos) - }; - - /// Clone the trie efficiently, via sharing. - /// - /// Purely-functional representation permits _O(1)_ copy, via persistent sharing. - public func clone(t : Trie) : Trie = t; - - /// Combine two nodes that may have a reduced size after an entry deletion. - func combineReducedNodes(left : Trie, right : Trie) : Trie = - switch (left, right) { - case (#empty, #empty) { - #empty - }; - case (#leaf _, #empty) { - left - }; - case (#empty, #leaf _) { - right - }; - case (#leaf leftLeaf, #leaf rightLeaf) { - let size = leftLeaf.size + rightLeaf.size; - if (size <= MAX_LEAF_SIZE) { - let union = List.append(leftLeaf.keyvals, rightLeaf.keyvals); - #leaf { size; keyvals = union } - } else { - branch(left, right) - } - }; - case (left, right) { - branch(left, right) - } - }; - - /// Replace the given key's value option with the given value, returning the modified trie. - /// Also returns the replaced value if the key existed and `null` otherwise. - /// Compares keys using the provided function `k_eq`. - /// - /// Note: Replacing a key's value by `null` removes the key and also shrinks the trie. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "test", Text.equal, 1).0; - /// trie := Trie.replace(trie, key "test", Text.equal, 42).0; - /// assert (Trie.get(trie, key "hello", Text.equal) == ?42); - /// ``` - public func replace(t : Trie, k : Key, k_eq : (K, K) -> Bool, v : ?V) : (Trie, ?V) { - let key_eq = equalKey(k_eq); - var replacedValue: ?V = null; - - func recursiveReplace(t : Trie, bitpos : Nat) : Trie = - switch t { - case (#empty) { - let (kvs, _) = AssocList.replace(null, k, key_eq, v); - leaf(kvs, bitpos) - }; - case (#branch b) { - let bit = Hash.bit(k.hash, bitpos); - // rebuild either the left or right path with the (k, v) pair - if (not bit) { - let l = recursiveReplace(b.left, bitpos + 1); - combineReducedNodes(l, b.right) - } else { - let r = recursiveReplace(b.right, bitpos + 1); - combineReducedNodes(b.left, r) - } - }; - case (#leaf l) { - let (kvs2, oldValue) = AssocList.replace(l.keyvals, k, key_eq, v); - replacedValue := oldValue; - leaf(kvs2, bitpos) - } - }; - let newTrie = recursiveReplace(t, 0); - //assert(isValid(newTrie, false)); - (newTrie, replacedValue) - }; - - /// Put the given key's value in the trie; return the new trie, and the previous value associated with the key, if any. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// let previousValue = Trie.put(trie, key "hello", Text.equal, 33).1; // Returns ?42 - /// assert(previousValue == ?42); - /// ``` - public func put(t : Trie, k : Key, k_eq : (K, K) -> Bool, v : V) : (Trie, ?V) = - replace(t, k, k_eq, ?v); - - /// Get the value of the given key in the trie, or return null if nonexistent. - /// - /// For a more detailed overview of how to use a Trie, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// var value = Trie.get(trie, key "hello", Text.equal); // Returns `?42` - /// assert(value == ?42); - /// value := Trie.get(trie, key "world", Text.equal); // Returns `null` - /// assert(value == null); - /// ``` - public func get(t : Trie, k : Key, k_eq : (K, K) -> Bool) : ?V = find(t, k, k_eq); - - /// Find the given key's value in the trie, or return `null` if nonexistent - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// var value = Trie.find(trie, key "hello", Text.equal); // Returns `?42` - /// assert(value == ?42); - /// value := Trie.find(trie, key "world", Text.equal); // Returns `null` - /// assert(value == null); - /// ``` - public func find(t : Trie, k : Key, k_eq : (K, K) -> Bool) : ?V { - let key_eq = equalKey(k_eq); - func rec(t : Trie, bitpos : Nat) : ?V = - switch t { - case (#empty) { null }; - case (#leaf l) { - AssocList.find(l.keyvals, k, key_eq) - }; - case (#branch b) { - let bit = Hash.bit(k.hash, bitpos); - if (not bit) { - rec(b.left, bitpos + 1) - } else { - rec(b.right, bitpos + 1) - } - } - }; - rec(t, 0) - }; - - func splitAssocList(al : AssocList, V>, bitpos : Nat) : (AssocList, V>, AssocList, V>) = - List.partition( - al, - func((k : Key, _v : V)) : Bool = not Hash.bit(k.hash, bitpos) - ); - - func splitList(l : AssocList, V>, bitpos : Nat) : (Nat, AssocList, V>, Nat, AssocList, V>) { - func rec(l : AssocList, V>) : (Nat, AssocList, V>, Nat, AssocList, V>) = - switch l { - case null { (0, null, 0, null) }; - case (?((k, v), t)) { - let (cl, l, cr, r) = rec(t); - if (not Hash.bit(k.hash, bitpos)) { (cl + 1, ?((k, v), l), cr, r) } else { - (cl, l, cr + 1, ?((k, v), r)) - } - } - }; - rec(l) - }; - - /// Merge tries, preferring the left trie where there are collisions - /// in common keys. - /// - /// note: the `disj` operation generalizes this `merge` - /// operation in various ways, and does not (in general) lose - /// information; this operation is a simpler, special case. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// trie := Trie.put(trie, key "bye", Text.equal, 42).0; - /// // trie2 is a copy of trie - /// var trie2 = Trie.clone(trie); - /// // trie2 has a different value for "hello" - /// trie2 := Trie.put(trie2, key "hello", Text.equal, 33).0; - /// // mergedTrie has the value 42 for "hello", as the left trie is preferred - /// // in the case of a collision - /// var mergedTrie = Trie.merge(trie, trie2, Text.equal); - /// var value = Trie.get(mergedTrie, key "hello", Text.equal); - /// assert(value == ?42); - /// ``` - public func merge(tl : Trie, tr : Trie, k_eq : (K, K) -> Bool) : Trie { - let key_eq = equalKey(k_eq); - func rec(bitpos : Nat, tl : Trie, tr : Trie) : Trie = - switch (tl, tr) { - case (#empty, _) { return tr }; - case (_, #empty) { return tl }; - case (#leaf l1, #leaf l2) { - leaf( - AssocList.disj( - l1.keyvals, - l2.keyvals, - key_eq, - func(x : ?V, y : ?V) : V = - switch (x, y) { - case (null, null) { P.unreachable() }; - case (null, ?v) { v }; - case (?v, _) { v } - } - ), - bitpos - ) - }; - case (#leaf l, _) { - let (ll, lr) = splitAssocList(l.keyvals, bitpos); - rec(bitpos, branch(leaf(ll, bitpos), leaf(lr, bitpos)), tr) - }; - case (_, #leaf l) { - let (ll, lr) = splitAssocList(l.keyvals, bitpos); - rec(bitpos, tl, branch(leaf(ll, bitpos), leaf(lr, bitpos))) - }; - case (#branch b1, #branch b2) { - branch( - rec(bitpos + 1, b1.left, b2.left), - rec(bitpos + 1, b1.right, b2.right) - ) - } - }; - rec(0, tl, tr) - }; - - /// - /// - /// Merge tries like `merge`, but traps if there are collisions in common keys between the - /// left and right inputs. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// trie := Trie.put(trie, key "bye", Text.equal, 42).0; - /// // trie2 is a copy of trie - /// var trie2 = Trie.clone(trie); - /// // trie2 has a different value for "hello" - /// trie2 := Trie.put(trie2, key "hello", Text.equal, 33).0; - /// // `mergeDisjoint` signals a dynamic errror - /// // in the case of a collision - /// var mergedTrie = Trie.mergeDisjoint(trie, trie2, Text.equal); - /// ``` - public func mergeDisjoint(tl : Trie, tr : Trie, k_eq : (K, K) -> Bool) : Trie { - func rec(bitpos : Nat, tl : Trie, tr : Trie) : Trie = - switch (tl, tr) { - case (#empty, _) { return tr }; - case (_, #empty) { return tl }; - case (#leaf l1, #leaf l2) { - leaf( - AssocList.disj( - l1.keyvals, - l2.keyvals, - equalKey(k_eq), - func(x : ?V, y : ?V) : V = - switch (x, y) { - case (null, ?v) { v }; - case (?v, null) { v }; - case (_, _) { Debug.trap "Trie.mergeDisjoint" } - } - ), - bitpos - ) - }; - case (#leaf l, _) { - let (ll, lr) = splitAssocList(l.keyvals, bitpos); - rec(bitpos, branch(leaf(ll, bitpos), leaf(lr, bitpos)), tr) - }; - case (_, #leaf l) { - let (ll, lr) = splitAssocList(l.keyvals, bitpos); - rec(bitpos, tl, branch(leaf(ll, bitpos), leaf(lr, bitpos))) - }; - case (#branch b1, #branch b2) { - branch( - rec(bitpos + 1, b1.left, b2.left), - rec(bitpos + 1, b1.right, b2.right) - ) - } - }; - rec(0, tl, tr) - }; - - /// Difference of tries. The output consists of pairs of - /// the left trie whose keys are not present in the right trie; the - /// values of the right trie are irrelevant. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// trie := Trie.put(trie, key "bye", Text.equal, 42).0; - /// // trie2 is a copy of trie - /// var trie2 = Trie.clone(trie); - /// // trie2 now has an additional key - /// trie2 := Trie.put(trie2, key "ciao", Text.equal, 33).0; - /// // `diff` returns a trie with the key "ciao", - /// // as this key is not present in `trie` - /// // (note that we pass `trie2` as the left trie) - /// Trie.diff(trie2, trie, Text.equal); - /// ``` - public func diff(tl : Trie, tr : Trie, k_eq : (K, K) -> Bool) : Trie { - let key_eq = equalKey(k_eq); - - func rec(bitpos : Nat, tl : Trie, tr : Trie) : Trie = - switch (tl, tr) { - case (#empty, _) { return #empty }; - case (_, #empty) { return tl }; - case (#leaf l1, #leaf l2) { - leaf( - AssocList.diff( - l1.keyvals, - l2.keyvals, - key_eq - ), - bitpos - ) - }; - case (#leaf l, _) { - let (ll, lr) = splitAssocList(l.keyvals, bitpos); - rec(bitpos, branch(leaf(ll, bitpos), leaf(lr, bitpos)), tr) - }; - case (_, #leaf l) { - let (ll, lr) = splitAssocList(l.keyvals, bitpos); - rec(bitpos, tl, branch(leaf(ll, bitpos), leaf(lr, bitpos))) - }; - case (#branch b1, #branch b2) { - branch( - rec(bitpos + 1, b1.left, b2.left), - rec(bitpos + 1, b1.right, b2.right) - ) - } - }; - rec(0, tl, tr) - }; - - /// Map disjunction. - /// - /// This operation generalizes the notion of "set union" to finite maps. - /// - /// Produces a "disjunctive image" of the two tries, where the values of - /// matching keys are combined with the given binary operator. - /// - /// For unmatched key-value pairs, the operator is still applied to - /// create the value in the image. To accomodate these various - /// situations, the operator accepts optional values, but is never - /// applied to (null, null). - /// - /// Implements the database idea of an ["outer join"](https://stackoverflow.com/questions/38549/what-is-the-difference-between-inner-join-and-outer-join). - /// - public func disj( - tl : Trie, - tr : Trie, - k_eq : (K, K) -> Bool, - vbin : (?V, ?W) -> X - ) : Trie { - let key_eq = equalKey(k_eq); - - /* empty right case; build from left only: */ - func recL(t : Trie, bitpos : Nat) : Trie = - switch t { - case (#empty) { #empty }; - case (#leaf l) { - leaf(AssocList.disj(l.keyvals, null, key_eq, vbin), bitpos) - }; - case (#branch b) { - branch( - recL(b.left, bitpos + 1), - recL(b.right, bitpos + 1) - ) - } - }; - - /* empty left case; build from right only: */ - func recR(t : Trie, bitpos : Nat) : Trie = - switch t { - case (#empty) { #empty }; - case (#leaf l) { - leaf(AssocList.disj(null, l.keyvals, key_eq, vbin), bitpos) - }; - case (#branch b) { - branch( - recR(b.left, bitpos + 1), - recR(b.right, bitpos + 1) - ) - } - }; - - /* main recursion */ - func rec(bitpos : Nat, tl : Trie, tr : Trie) : Trie = - switch (tl, tr) { - case (#empty, #empty) { #empty }; - case (#empty, _) { recR(tr, bitpos) }; - case (_, #empty) { recL(tl, bitpos) }; - case (#leaf l1, #leaf l2) { - leaf(AssocList.disj(l1.keyvals, l2.keyvals, key_eq, vbin), bitpos) - }; - case (#leaf l, _) { - let (ll, lr) = splitAssocList(l.keyvals, bitpos); - rec(bitpos, branch(leaf(ll, bitpos), leaf(lr, bitpos)), tr) - }; - case (_, #leaf l) { - let (ll, lr) = splitAssocList(l.keyvals, bitpos); - rec(bitpos, tl, branch(leaf(ll, bitpos), leaf(lr, bitpos))) - }; - case (#branch b1, #branch b2) { - branch( - rec(bitpos + 1, b1.left, b2.left), - rec(bitpos + 1, b1.right, b2.right) - ) - } - }; - - rec(0, tl, tr) - }; - - /// Map join. - /// - /// Implements the database idea of an ["inner join"](https://stackoverflow.com/questions/38549/what-is-the-difference-between-inner-join-and-outer-join). - /// - /// This operation generalizes the notion of "set intersection" to - /// finite maps. The values of matching keys are combined with the given binary - /// operator, and unmatched key-value pairs are not present in the output. - /// - public func join( - tl : Trie, - tr : Trie, - k_eq : (K, K) -> Bool, - vbin : (V, W) -> X - ) : Trie { - let key_eq = equalKey(k_eq); - - func rec(bitpos : Nat, tl : Trie, tr : Trie) : Trie = - switch (tl, tr) { - case (#empty, _) { #empty }; - case (_, #empty) { #empty }; - case (#leaf l1, #leaf l2) { - leaf(AssocList.join(l1.keyvals, l2.keyvals, key_eq, vbin), bitpos) - }; - case (#leaf l, _) { - let (ll, lr) = splitAssocList(l.keyvals, bitpos); - rec(bitpos, branch(leaf(ll, bitpos), leaf(lr, bitpos)), tr) - }; - case (_, #leaf l) { - let (ll, lr) = splitAssocList(l.keyvals, bitpos); - rec(bitpos, tl, branch(leaf(ll, bitpos), leaf(lr, bitpos))) - }; - case (#branch b1, #branch b2) { - branch( - rec(bitpos + 1, b1.left, b2.left), - rec(bitpos + 1, b1.right, b2.right) - ) - } - }; - - rec(0, tl, tr) - }; - - /// This operation gives a recursor for the internal structure of - /// tries. Many common operations are instantiations of this function, - /// either as clients, or as hand-specialized versions (e.g., see , map, - /// mapFilter, some and all below). - public func foldUp(t : Trie, bin : (X, X) -> X, leaf : (K, V) -> X, empty : X) : X { - func rec(t : Trie) : X = - switch t { - case (#empty) { empty }; - case (#leaf l) { - AssocList.fold( - l.keyvals, - empty, - func(k : Key, v : V, x : X) : X = bin(leaf(k.key, v), x) - ) - }; - case (#branch b) { bin(rec(b.left), rec(b.right)) } - }; - rec(t) - }; - - /// Map product. - /// - /// Conditional _catesian product_, where the given - /// operation `op` _conditionally_ creates output elements in the - /// resulting trie. - /// - /// The keyed structure of the input tries are not relevant for this - /// operation: all pairs are considered, regardless of keys matching or - /// not. Moreover, the resulting trie may use keys that are unrelated to - /// these input keys. - /// - public func prod( - tl : Trie, - tr : Trie, - op : (K1, V1, K2, V2) -> ?(Key, V3), - k3_eq : (K3, K3) -> Bool - ) : Trie { - - /*- binary case: merge disjoint results: */ - func merge(a : Trie, b : Trie) : Trie = mergeDisjoint(a, b, k3_eq); - - /*- "`foldUp` squared" (imagine two nested loops): */ - foldUp( - tl, - merge, - func(k1 : K1, v1 : V1) : Trie = - foldUp( - tr, - merge, - func(k2 : K2, v2 : V2) : Trie = - switch (op(k1, v1, k2, v2)) { - case null { #empty }; - case (?(k3, v3)) { put(#empty, k3, k3_eq, v3).0 } - }, - #empty - ), - #empty - ) - }; - - /// Returns an iterator of type `Iter` over the key-value entries of the trie. - /// - /// Each iterator gets a _persistent view_ of the mapping, independent of concurrent updates to the iterated map. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// trie := Trie.put(trie, key "bye", Text.equal, 32).0; - /// // create an Iterator over key-value pairs of trie - /// let iter = Trie.iter(trie); - /// // add another key-value pair to `trie`. - /// // because we created our iterator before - /// // this update, it will not contain this new key-value pair - /// trie := Trie.put(trie, key "ciao", Text.equal, 3).0; - /// var sum : Nat = 0; - /// for ((k,v) in iter) { - /// sum += v; - /// }; - /// assert(sum == 74); - /// ``` - public func iter(t : Trie) : I.Iter<(K, V)> = - object { - var stack = ?(t, null) : List.List>; - public func next() : ?(K, V) = - switch stack { - case null { null }; - case (?(trie, stack2)) { - switch trie { - case (#empty) { - stack := stack2; - next() - }; - case (#leaf { keyvals = null }) { - stack := stack2; - next() - }; - case (#leaf { size = c; keyvals = ?((k, v), kvs) }) { - stack := ?(#leaf { size = c - 1; keyvals = kvs }, stack2); - ?(k.key, v) - }; - case (#branch br) { - stack := ?(br.left, ?(br.right, stack2)); - next() - } - } - } - } - }; - - /// Represent the construction of tries as data. - /// - /// This module provides optimized variants of normal tries, for - /// more efficient join queries. - /// - /// The central insight is that for (unmaterialized) join query results, we - /// do not need to actually build any resulting trie of the resulting - /// data, but rather, just need a collection of what would be in that - /// trie. Since query results can be large (quadratic in the DB size), - /// avoiding the construction of this trie provides a considerable savings. - /// - /// To get this savings, we use an ADT for the operations that _would_ build this trie, - /// if evaluated. This structure specializes a rope: a balanced tree representing a - /// sequence. It is only as balanced as the tries from which we generate - /// these build ASTs. They have no intrinsic balance properties of their - /// own. - /// - public module Build { - /// The build of a trie, as an AST for a simple DSL. - public type Build = { - #skip; - #put : (K, ?Hash.Hash, V); - #seq : { - size : Nat; - left : Build; - right : Build - } - }; - - /// Size of the build, measured in `#put` operations - public func size(tb : Build) : Nat = - switch tb { - case (#skip) { 0 }; - case (#put(_, _, _)) { 1 }; - case (#seq(seq)) { seq.size } - }; - - /// Build sequence of two sub-builds - public func seq(l : Build, r : Build) : Build { - let sum = size(l) + size(r); - #seq { size = sum; left = l; right = r } - }; - - /// Like [`prod`](#prod), except do not actually do the put calls, just - /// record them, as a (binary tree) data structure, isomorphic to the - /// recursion of this function (which is balanced, in expectation). - public func prod( - tl : Trie, - tr : Trie, - op : (K1, V1, K2, V2) -> ?(K3, V3), - _k3_eq : (K3, K3) -> Bool - ) : Build { - - func bin(a : Build, b : Build) : Build = seq(a, b); - - /// double-nested folds - foldUp( - tl, - bin, - func(k1 : K1, v1 : V1) : Build = - foldUp( - tr, - bin, - func(k2 : K2, v2 : V2) : Build = - switch (op(k1, v1, k2, v2)) { - case null { #skip }; - case (?(k3, v3)) { #put(k3, null, v3) } - }, - #skip - ), - #skip - ) - }; - - /// Project the nth key-value pair from the trie build. - /// - /// This position is meaningful only when the build contains multiple uses of one or more keys, otherwise it is not. - public func nth(tb : Build, i : Nat) : ?(K, ?Hash.Hash, V) { - func rec(tb : Build, i : Nat) : ?(K, ?Hash.Hash, V) = - switch tb { - case (#skip) { P.unreachable() }; - case (#put(k, h, v)) { - assert (i == 0); - ?(k, h, v) - }; - case (#seq(s)) { - let size_left = size(s.left); - if (i < size_left) { rec(s.left, i) } else { - rec(s.right, i - size_left) - } - } - }; - - if (i >= size(tb)) { - return null - }; - rec(tb, i) - }; - - /// Like [`mergeDisjoint`](#mergedisjoint), except that it avoids the - /// work of actually merging any tries; rather, just record the work for - /// latter (if ever). - public func projectInner(t : Trie>) : Build = - foldUp( - t, - func(t1 : Build, t2 : Build) : Build = seq(t1, t2), - func(_ : K1, t : Build) : Build = t, - #skip - ); - - /// Gather the collection of key-value pairs into an array of a (possibly-distinct) type. - public func toArray(tb : Build, f : (K, V) -> W) : [W] { - let c = size(tb); - let a = A.init(c, null); - var i = 0; - func rec(tb : Build) = - switch tb { - case (#skip) {}; - case (#put(k, _, v)) { a[i] := ?f(k, v); i := i + 1 }; - case (#seq(s)) { rec(s.left); rec(s.right) } - }; - rec(tb); - A.tabulate( - c, - func(i : Nat) : W = - switch (a[i]) { - case null { P.unreachable() }; - case (?x) { x } - } - ) - }; - - }; - - /// Fold over the key-value pairs of the trie, using an accumulator. - /// The key-value pairs have no reliable or meaningful ordering. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// trie := Trie.put(trie, key "bye", Text.equal, 32).0; - /// trie := Trie.put(trie, key "ciao", Text.equal, 3).0; - /// // create an accumulator, in our case the sum of all values - /// func calculateSum(k : Text, v : Nat, acc : Nat) : Nat = acc + v; - /// // Fold over the trie using the accumulator. - /// // Note that 0 is the initial value of the accumulator. - /// let sum = Trie.fold(trie, calculateSum, 0); - /// assert(sum == 77); - /// ``` - public func fold(t : Trie, f : (K, V, X) -> X, x : X) : X { - func rec(t : Trie, x : X) : X = - switch t { - case (#empty) { x }; - case (#leaf l) { - AssocList.fold( - l.keyvals, - x, - func(k : Key, v : V, x : X) : X = f(k.key, v, x) - ) - }; - case (#branch b) { rec(b.left, rec(b.right, x)) } - }; - rec(t, x) - }; - - /// Test whether a given key-value pair is present, or not. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// trie := Trie.put(trie, key "bye", Text.equal, 32).0; - /// trie := Trie.put(trie, key "ciao", Text.equal, 3).0; - /// // `some` takes a function that returns a Boolean indicating whether - /// // the key-value pair is present or not - /// var isPresent = Trie.some( - /// trie, - /// func(k : Text, v : Nat) : Bool = k == "bye" and v == 32, - /// ); - /// assert(isPresent == true); - /// isPresent := Trie.some( - /// trie, - /// func(k : Text, v : Nat) : Bool = k == "hello" and v == 32, - /// ); - /// assert(isPresent == false); - /// ``` - public func some(t : Trie, f : (K, V) -> Bool) : Bool { - func rec(t : Trie) : Bool = - switch t { - case (#empty) { false }; - case (#leaf l) { - List.some( - l.keyvals, - func((k : Key, v : V)) : Bool = f(k.key, v) - ) - }; - case (#branch b) { rec(b.left) or rec(b.right) } - }; - rec(t) - }; - - /// Test whether all key-value pairs have a given property. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// trie := Trie.put(trie, key "bye", Text.equal, 32).0; - /// trie := Trie.put(trie, key "ciao", Text.equal, 10).0; - /// // `all` takes a function that returns a boolean indicating whether - /// // the key-value pairs all have a given property, in our case that - /// // all values are greater than 9 - /// var hasProperty = Trie.all( - /// trie, - /// func(k : Text, v : Nat) : Bool = v > 9, - /// ); - /// assert(hasProperty == true); - /// // now we check if all values are greater than 100 - /// hasProperty := Trie.all( - /// trie, - /// func(k : Text, v : Nat) : Bool = v > 100, - /// ); - /// assert(hasProperty == false); - /// ``` - public func all(t : Trie, f : (K, V) -> Bool) : Bool { - func rec(t : Trie) : Bool = - switch t { - case (#empty) { true }; - case (#leaf l) { - List.all( - l.keyvals, - func((k : Key, v : V)) : Bool = f(k.key, v) - ) - }; - case (#branch b) { rec(b.left) and rec(b.right) } - }; - rec(t) - }; - - /// Project the nth key-value pair from the trie. - /// - /// Note: This position is not meaningful; it's only here so that we - /// can inject tries into arrays using functions like `Array.tabulate`. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// import Array "mo:base/Array"; - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// trie := Trie.put(trie, key "bye", Text.equal, 32).0; - /// trie := Trie.put(trie, key "ciao", Text.equal, 10).0; - /// // `tabulate` takes a size parameter, so we check the size of - /// // the trie first - /// let size = Trie.size(trie); - /// // Now we can create an array of the same size passing `nth` as - /// // the generator used to fill the array. - /// // Note that `toArray` is a convenience function that does the - /// // same thing without you having to check whether the tuple is - /// // `null` or not, which we're not doing in this example - /// let array = Array.tabulate, Nat)>( - /// size, - /// func n = Trie.nth(trie, n) - /// ); - /// ``` - public func nth(t : Trie, i : Nat) : ?(Key, V) { - func rec(t : Trie, i : Nat) : ?(Key, V) = - switch t { - case (#empty) { P.unreachable() }; - case (#leaf l) { List.get(l.keyvals, i) }; - case (#branch b) { - let size_left = size(b.left); - if (i < size_left) { rec(b.left, i) } else { - rec(b.right, i - size_left) - } - } - }; - if (i >= size(t)) { - return null - }; - rec(t, i) - }; - - /// Gather the collection of key-value pairs into an array of a (possibly-distinct) type. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// trie := Trie.put(trie, key "bye", Text.equal, 32).0; - /// trie := Trie.put(trie, key "ciao", Text.equal, 10).0; - /// // `toArray` takes a function that takes a key-value tuple - /// // and returns a value of the type you want to use to fill - /// // the array. - /// // In our case we just return the value - /// let array = Trie.toArray( - /// trie, - /// func (k, v) = v - /// ); - /// ``` - public func toArray(t : Trie, f : (K, V) -> W) : [W] = - A.tabulate( - size(t), - func(i : Nat) : W { - let (k, v) = switch (nth(t, i)) { - case null { P.unreachable() }; - case (?x) { x } - }; - f(k.key, v) - } - ); - - /// Test for "deep emptiness": subtrees that have branching structure, - /// but no leaves. These can result from naive filtering operations; - /// filter uses this function to avoid creating such subtrees. - public func isEmpty(t : Trie) : Bool = size(t) == 0; - - /// Filter the key-value pairs by a given predicate. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// trie := Trie.put(trie, key "bye", Text.equal, 32).0; - /// trie := Trie.put(trie, key "ciao", Text.equal, 10).0; - /// // `filter` takes a function that takes a key-value tuple - /// // and returns true if the key-value pair should be included. - /// // In our case those are pairs with a value greater than 20 - /// let filteredTrie = Trie.filter( - /// trie, - /// func (k, v) = v > 20 - /// ); - /// assert (Trie.all(filteredTrie, func(k, v) = v > 20) == true); - /// ``` - public func filter(t : Trie, f : (K, V) -> Bool) : Trie { - func rec(t : Trie, bitpos : Nat) : Trie = - switch t { - case (#empty) { #empty }; - case (#leaf l) { - leaf( - List.filter( - l.keyvals, - func((k : Key, v : V)) : Bool = f(k.key, v) - ), - bitpos - ) - }; - case (#branch b) { - let fl = rec(b.left, bitpos + 1); - let fr = rec(b.right, bitpos + 1); - combineReducedNodes(fl, fr) - } - }; - rec(t, 0) - }; - - /// Map and filter the key-value pairs by a given predicate. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// trie := Trie.put(trie, key "bye", Text.equal, 32).0; - /// trie := Trie.put(trie, key "ciao", Text.equal, 10).0; - /// // `mapFilter` takes a function that takes a key-value tuple - /// // and returns a possibly-distinct value if the key-value pair should be included. - /// // In our case, we filter for values greater than 20 and map them to their square. - /// let filteredTrie = Trie.mapFilter( - /// trie, - /// func (k, v) = if (v > 20) return ?(v**2) else return null - /// ); - /// assert (Trie.all(filteredTrie, func(k, v) = v > 60) == true); - /// ``` - public func mapFilter(t : Trie, f : (K, V) -> ?W) : Trie { - func rec(t : Trie, bitpos : Nat) : Trie = - switch t { - case (#empty) { #empty }; - case (#leaf l) { - leaf( - List.mapFilter( - l.keyvals, - // retain key and hash, but update key's value using f: - func((k : Key, v : V)) : ?(Key, W) = - switch (f(k.key, v)) { - case null { null }; - case (?w) { ?({ key = k.key; hash = k.hash }, w) } - } - ), - bitpos - ) - }; - case (#branch b) { - let fl = rec(b.left, bitpos + 1); - let fr = rec(b.right, bitpos + 1); - combineReducedNodes(fl, fr) - } - }; - - rec(t, 0) - }; - - /// Test for equality, but naively, based on structure. - /// Does not attempt to remove "junk" in the tree; - /// For instance, a "smarter" approach would equate - /// `#bin {left = #empty; right = #empty}` - /// with - /// `#empty`. - /// We do not observe that equality here. - public func equalStructure( - tl : Trie, - tr : Trie, - keq : (K, K) -> Bool, - veq : (V, V) -> Bool - ) : Bool { - func rec(tl : Trie, tr : Trie) : Bool = - switch (tl, tr) { - case (#empty, #empty) { true }; - case (#leaf l1, #leaf l2) { - List.equal( - l1.keyvals, - l2.keyvals, - func((k1 : Key, v1 : V), (k2 : Key, v2 : V)) : Bool = keq(k1.key, k2.key) and veq(v1, v2) - ) - }; - case (#branch b1, #branch b2) { - rec(b1.left, b2.left) and rec(b2.right, b2.right) - }; - case _ { false } - }; - rec(tl, tr) - }; - - /// Replace the given key's value in the trie, - /// and only if successful, do the success continuation, - /// otherwise, return the failure value - /// - /// For a more detailed overview of how to use a Trie, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// trie := Trie.put(trie, key "bye", Text.equal, 32).0; - /// trie := Trie.put(trie, key "ciao", Text.equal, 10).0; - /// // `replaceThen` takes the same arguments as `replace` but also a success continuation - /// // and a failure connection that are called in the respective scenarios. - /// // if the replace fails, that is the key is not present in the trie, the failure continuation is called. - /// // if the replace succeeds, that is the key is present in the trie, the success continuation is called. - /// // in this example we are simply returning the Text values `success` and `fail` respectively. - /// var continuation = Trie.replaceThen( - /// trie, - /// key "hello", - /// Text.equal, - /// 12, - /// func (t, v) = "success", - /// func () = "fail" - /// ); - /// assert (continuation == "success"); - /// continuation := Trie.replaceThen( - /// trie, - /// key "shalom", - /// Text.equal, - /// 12, - /// func (t, v) = "success", - /// func () = "fail" - /// ); - /// assert (continuation == "fail"); - /// ``` - public func replaceThen( - t : Trie, - k : Key, - k_eq : (K, K) -> Bool, - v2 : V, - success : (Trie, V) -> X, - fail : () -> X - ) : X { - let (t2, ov) = replace(t, k, k_eq, ?v2); - switch ov { - case null { /* no prior value; failure to remove */ fail() }; - case (?v1) { success(t2, v1) } - } - }; - - /// Put the given key's value in the trie; return the new trie; assert that no prior value is associated with the key - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// // note that compared to `put`, `putFresh` does not return a tuple - /// trie := Trie.putFresh(trie, key "hello", Text.equal, 42); - /// trie := Trie.putFresh(trie, key "bye", Text.equal, 32); - /// // this will fail as "hello" is already present in the trie - /// trie := Trie.putFresh(trie, key "hello", Text.equal, 10); - /// ``` - public func putFresh(t : Trie, k : Key, k_eq : (K, K) -> Bool, v : V) : Trie { - let (t2, none) = replace(t, k, k_eq, ?v); - switch none { - case null {}; - case (?_) assert false - }; - t2 - }; - - /// Put the given key's value in the 2D trie; return the new 2D trie. - public func put2D( - t : Trie2D, - k1 : Key, - k1_eq : (K1, K1) -> Bool, - k2 : Key, - k2_eq : (K2, K2) -> Bool, - v : V - ) : Trie2D { - let inner = find(t, k1, k1_eq); - let (updated_inner, _) = switch inner { - case null { put(#empty, k2, k2_eq, v) }; - case (?inner) { put(inner, k2, k2_eq, v) } - }; - let (updated_outer, _) = put(t, k1, k1_eq, updated_inner); - updated_outer - }; - - /// Put the given key's value in the trie; return the new trie; - public func put3D( - t : Trie3D, - k1 : Key, - k1_eq : (K1, K1) -> Bool, - k2 : Key, - k2_eq : (K2, K2) -> Bool, - k3 : Key, - k3_eq : (K3, K3) -> Bool, - v : V - ) : Trie3D { - let inner1 = find(t, k1, k1_eq); - let (updated_inner1, _) = switch inner1 { - case null { - put( - #empty, - k2, - k2_eq, - (put(#empty, k3, k3_eq, v)).0 - ) - }; - case (?inner1) { - let inner2 = find(inner1, k2, k2_eq); - let (updated_inner2, _) = switch inner2 { - case null { put(#empty, k3, k3_eq, v) }; - case (?inner2) { put(inner2, k3, k3_eq, v) } - }; - put(inner1, k2, k2_eq, updated_inner2) - } - }; - let (updated_outer, _) = put(t, k1, k1_eq, updated_inner1); - updated_outer - }; - - /// Remove the entry for the given key from the trie, by returning the reduced trie. - /// Also returns the removed value if the key existed and `null` otherwise. - /// Compares keys using the provided function `k_eq`. - /// - /// Note: The removal of an existing key shrinks the trie. - /// - /// For a more detailed overview of how to use a `Trie`, - /// see the [User's Overview](#overview). - /// - /// Example: - /// ```motoko include=initialize - /// trie := Trie.put(trie, key "hello", Text.equal, 42).0; - /// trie := Trie.put(trie, key "bye", Text.equal, 32).0; - /// // remove the entry associated with "hello" - /// trie := Trie.remove(trie, key "hello", Text.equal).0; - /// assert (Trie.get(trie, key "hello", Text.equal) == null); - /// ``` - public func remove(t : Trie, k : Key, k_eq : (K, K) -> Bool) : (Trie, ?V) = - replace(t, k, k_eq, null); - - /// Remove the given key's value in the trie, - /// and only if successful, do the success continuation, - /// otherwise, return the failure value - public func removeThen( - t : Trie, - k : Key, - k_eq : (K, K) -> Bool, - success : (Trie, V) -> X, - fail : () -> X - ) : X { - let (t2, ov) = replace(t, k, k_eq, null); - switch ov { - case null { /* no prior value; failure to remove */ fail() }; - case (?v) { success(t2, v) } - } - }; - - /// remove the given key-key pair's value in the 2D trie; return the - /// new trie, and the prior value, if any. - public func remove2D( - t : Trie2D, - k1 : Key, - k1_eq : (K1, K1) -> Bool, - k2 : Key, - k2_eq : (K2, K2) -> Bool - ) : (Trie2D, ?V) = - switch (find(t, k1, k1_eq)) { - case null { (t, null) }; - case (?inner) { - let (updated_inner, ov) = remove(inner, k2, k2_eq); - let (updated_outer, _) = put(t, k1, k1_eq, updated_inner); - (updated_outer, ov) - } - }; - - /// Remove the given key-key pair's value in the 3D trie; return the - /// new trie, and the prior value, if any. - public func remove3D( - t : Trie3D, - k1 : Key, - k1_eq : (K1, K1) -> Bool, - k2 : Key, - k2_eq : (K2, K2) -> Bool, - k3 : Key, - k3_eq : (K3, K3) -> Bool - ) : (Trie3D, ?V) = - switch (find(t, k1, k1_eq)) { - case null { (t, null) }; - case (?inner) { - let (updated_inner, ov) = remove2D(inner, k2, k2_eq, k3, k3_eq); - let (updated_outer, _) = put(t, k1, k1_eq, updated_inner); - (updated_outer, ov) - } - }; - - /// Like [`mergeDisjoint`](#mergedisjoint), except instead of merging a - /// pair, it merges the collection of dimension-2 sub-trees of a 2D - /// trie. - public func mergeDisjoint2D( - t : Trie2D, - _k1_eq : (K1, K1) -> Bool, - k2_eq : (K2, K2) -> Bool - ) : Trie = - foldUp( - t, - func(t1 : Trie, t2 : Trie) : Trie = mergeDisjoint(t1, t2, k2_eq), - func(_ : K1, t : Trie) : Trie = t, - #empty - ); - -} diff --git a/.mops/base@0.11.1/src/TrieMap.mo b/.mops/base@0.11.1/src/TrieMap.mo deleted file mode 100644 index 29b00ce..0000000 --- a/.mops/base@0.11.1/src/TrieMap.mo +++ /dev/null @@ -1,396 +0,0 @@ -/// Class `TrieMap` provides a map from keys of type `K` to values of type `V`. -/// The class wraps and manipulates an underyling hash trie, found in the `Trie` -/// module. The trie is a binary tree in which the position of elements in the -/// tree are determined using the hash of the elements. -/// -/// Note: The `class` `TrieMap` exposes the same interface as `HashMap`. -/// -/// Creating a map: -/// The equality function is used to compare keys, and the hash function is used -/// to hash keys. See the example below. -/// -/// ```motoko name=initialize -/// import TrieMap "mo:base/TrieMap"; -/// import Nat "mo:base/Nat"; -/// import Hash "mo:base/Hash"; -/// import Iter "mo:base/Iter"; -/// -/// let map = TrieMap.TrieMap(Nat.equal, Hash.hash) -/// ``` - -import T "Trie"; -import P "Prelude"; -import I "Iter"; -import Hash "Hash"; -import List "List"; - -module { - public class TrieMap(isEq : (K, K) -> Bool, hashOf : K -> Hash.Hash) { - var map = T.empty(); - var _size : Nat = 0; - - /// Returns the number of entries in the map. - /// - /// Example: - /// ```motoko include=initialize - /// map.size() - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func size() : Nat { _size }; - - /// Maps `key` to `value`, and overwrites the old entry if the key - /// was already present. - /// - /// Example: - /// ```motoko include=initialize - /// map.put(0, 10); - /// map.put(2, 12); - /// Iter.toArray(map.entries()) - /// ``` - /// - /// Runtime: O(log(size)) - /// Space: O(log(size)) - /// - /// *Runtime and space assumes that the trie is reasonably balanced and the - /// map is using a constant time and space equality and hash function. - public func put(key : K, value : V) = ignore replace(key, value); - - /// Maps `key` to `value`. Overwrites _and_ returns the old entry as an - /// option if the key was already present, and `null` otherwise. - /// - /// Example: - /// ```motoko include=initialize - /// map.put(0, 10); - /// map.replace(0, 20) - /// ``` - /// - /// Runtime: O(log(size)) - /// Space: O(log(size)) - /// - /// *Runtime and space assumes that the trie is reasonably balanced and the - /// map is using a constant time and space equality and hash function. - public func replace(key : K, value : V) : ?V { - let keyObj = { key; hash = hashOf(key) }; - let (map2, ov) = T.put(map, keyObj, isEq, value); - map := map2; - switch (ov) { - case null { _size += 1 }; - case _ {} - }; - ov - }; - - /// Gets the value associated with the key `key` in an option, or `null` if it - /// doesn't exist. - /// - /// Example: - /// ```motoko include=initialize - /// map.put(0, 10); - /// map.get(0) - /// ``` - /// - /// Runtime: O(log(size)) - /// Space: O(log(size)) - /// - /// *Runtime and space assumes that the trie is reasonably balanced and the - /// map is using a constant time and space equality and hash function. - public func get(key : K) : ?V { - let keyObj = { key; hash = hashOf(key) }; - T.find(map, keyObj, isEq) - }; - - /// Delete the entry associated with key `key`, if it exists. If the key is - /// absent, there is no effect. - /// - /// Note: The deletion of an existing key shrinks the trie map. - /// - /// Example: - /// ```motoko include=initialize - /// map.put(0, 10); - /// map.delete(0); - /// map.get(0) - /// ``` - /// - /// Runtime: O(log(size)) - /// Space: O(log(size)) - /// - /// *Runtime and space assumes that the trie is reasonably balanced and the - /// map is using a constant time and space equality and hash function. - public func delete(key : K) = ignore remove(key); - - /// Delete the entry associated with key `key`. Return the deleted value - /// as an option if it exists, and `null` otherwise. - /// - /// Note: The deletion of an existing key shrinks the trie map. - /// - /// Example: - /// ```motoko include=initialize - /// map.put(0, 10); - /// map.remove(0) - /// ``` - /// - /// Runtime: O(log(size)) - /// Space: O(log(size)) - /// - /// *Runtime and space assumes that the trie is reasonably balanced and the - /// map is using a constant time and space equality and hash function. - public func remove(key : K) : ?V { - let keyObj = { key; hash = hashOf(key) }; - let (t, ov) = T.remove(map, keyObj, isEq); - map := t; - switch (ov) { - case null {}; - case (?_) { _size -= 1 } - }; - ov - }; - - /// Returns an iterator over the keys of the map. - /// - /// Each iterator gets a _snapshot view_ of the mapping, and is unaffected - /// by concurrent updates to the iterated map. - /// - /// Example: - /// ```motoko include=initialize - /// map.put(0, 10); - /// map.put(1, 11); - /// map.put(2, 12); - /// - /// // find the sum of all the keys - /// var sum = 0; - /// for (key in map.keys()) { - /// sum += key; - /// }; - /// // 0 + 1 + 2 - /// sum - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - /// - /// *The above runtime and space are for the construction of the iterator. - /// The iteration itself takes linear time and logarithmic space to execute. - public func keys() : I.Iter { - I.map(entries(), func(kv : (K, V)) : K { kv.0 }) - }; - - /// Returns an iterator over the values in the map. - /// - /// Each iterator gets a _snapshot view_ of the mapping, and is unaffected - /// by concurrent updates to the iterated map. - /// - /// Example: - /// ```motoko include=initialize - /// map.put(0, 10); - /// map.put(1, 11); - /// map.put(2, 12); - /// - /// // find the sum of all the values - /// var sum = 0; - /// for (key in map.vals()) { - /// sum += key; - /// }; - /// // 10 + 11 + 12 - /// sum - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - /// - /// *The above runtime and space are for the construction of the iterator. - /// The iteration itself takes linear time and logarithmic space to execute. - public func vals() : I.Iter { - I.map(entries(), func(kv : (K, V)) : V { kv.1 }) - }; - - /// Returns an iterator over the entries (key-value pairs) in the map. - /// - /// Each iterator gets a _snapshot view_ of the mapping, and is unaffected - /// by concurrent updates to the iterated map. - /// - /// Example: - /// ```motoko include=initialize - /// map.put(0, 10); - /// map.put(1, 11); - /// map.put(2, 12); - /// - /// // find the sum of all the products of key-value pairs - /// var sum = 0; - /// for ((key, value) in map.entries()) { - /// sum += key * value; - /// }; - /// // (0 * 10) + (1 * 11) + (2 * 12) - /// sum - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - /// - /// *The above runtime and space are for the construction of the iterator. - /// The iteration itself takes linear time and logarithmic space to execute. - public func entries() : I.Iter<(K, V)> { - object { - var stack = ?(map, null) : List.List>; - public func next() : ?(K, V) { - switch stack { - case null { null }; - case (?(trie, stack2)) { - switch trie { - case (#empty) { - stack := stack2; - next() - }; - case (#leaf({ keyvals = null })) { - stack := stack2; - next() - }; - case (#leaf({ size = c; keyvals = ?((k, v), kvs) })) { - stack := ?(#leaf({ size = c -1; keyvals = kvs }), stack2); - ?(k.key, v) - }; - case (#branch(br)) { - stack := ?(br.left, ?(br.right, stack2)); - next() - } - } - } - } - } - } - } - }; - - /// Produce a copy of `map`, using `keyEq` to compare keys and `keyHash` to - /// hash keys. - /// - /// Example: - /// ```motoko include=initialize - /// map.put(0, 10); - /// map.put(1, 11); - /// map.put(2, 12); - /// // Clone using the same equality and hash functions used to initialize `map` - /// let mapCopy = TrieMap.clone(map, Nat.equal, Hash.hash); - /// Iter.toArray(mapCopy.entries()) - /// ``` - /// - /// Runtime: O(size * log(size)) - /// Space: O(size) - /// - /// *Runtime and space assumes that the trie underlying `map` is reasonably - /// balanced and that `keyEq` and `keyHash` run in O(1) time and space. - public func clone( - map : TrieMap, - keyEq : (K, K) -> Bool, - keyHash : K -> Hash.Hash - ) : TrieMap { - let h2 = TrieMap(keyEq, keyHash); - for ((k, v) in map.entries()) { - h2.put(k, v) - }; - h2 - }; - - /// Create a new map from the entries in `entries`, using `keyEq` to compare - /// keys and `keyHash` to hash keys. - /// - /// Example: - /// ```motoko include=initialize - /// let entries = [(0, 10), (1, 11), (2, 12)]; - /// let newMap = TrieMap.fromEntries(entries.vals(), Nat.equal, Hash.hash); - /// newMap.get(2) - /// ``` - /// - /// Runtime: O(size * log(size)) - /// Space: O(size) - /// - /// *Runtime and space assumes that `entries` returns elements in O(1) time, - /// and `keyEq` and `keyHash` run in O(1) time and space. - public func fromEntries( - entries : I.Iter<(K, V)>, - keyEq : (K, K) -> Bool, - keyHash : K -> Hash.Hash - ) : TrieMap { - let h = TrieMap(keyEq, keyHash); - for ((k, v) in entries) { - h.put(k, v) - }; - h - }; - - /// Transform (map) the values in `map` using function `f`, retaining the keys. - /// Uses `keyEq` to compare keys and `keyHash` to hash keys. - /// - /// Example: - /// ```motoko include=initialize - /// map.put(0, 10); - /// map.put(1, 11); - /// map.put(2, 12); - /// // double all the values in map - /// let newMap = TrieMap.map(map, Nat.equal, Hash.hash, func(key, value) = value * 2); - /// Iter.toArray(newMap.entries()) - /// ``` - /// - /// Runtime: O(size * log(size)) - /// Space: O(size) - /// - /// *Runtime and space assumes that `f`, `keyEq`, and `keyHash` run in O(1) - /// time and space. - public func map( - map : TrieMap, - keyEq : (K, K) -> Bool, - keyHash : K -> Hash.Hash, - f : (K, V1) -> V2 - ) : TrieMap { - let h2 = TrieMap(keyEq, keyHash); - for ((k, v1) in map.entries()) { - let v2 = f(k, v1); - h2.put(k, v2) - }; - h2 - }; - - /// Transform (map) the values in `map` using function `f`, discarding entries - /// for which `f` evaluates to `null`. Uses `keyEq` to compare keys and - /// `keyHash` to hash keys. - /// - /// Example: - /// ```motoko include=initialize - /// map.put(0, 10); - /// map.put(1, 11); - /// map.put(2, 12); - /// // double all the values in map, only keeping entries that have an even key - /// let newMap = - /// TrieMap.mapFilter( - /// map, - /// Nat.equal, - /// Hash.hash, - /// func(key, value) = if (key % 2 == 0) { ?(value * 2) } else { null } - /// ); - /// Iter.toArray(newMap.entries()) - /// ``` - /// - /// Runtime: O(size * log(size)) - /// Space: O(size) - /// - /// *Runtime and space assumes that `f`, `keyEq`, and `keyHash` run in O(1) - /// time and space. - public func mapFilter( - map : TrieMap, - keyEq : (K, K) -> Bool, - keyHash : K -> Hash.Hash, - f : (K, V1) -> ?V2 - ) : TrieMap { - let h2 = TrieMap(keyEq, keyHash); - for ((k, v1) in map.entries()) { - switch (f(k, v1)) { - case null {}; - case (?v2) { - h2.put(k, v2) - } - } - }; - h2 - } -} diff --git a/.mops/base@0.11.1/src/TrieSet.mo b/.mops/base@0.11.1/src/TrieSet.mo deleted file mode 100644 index 7638af3..0000000 --- a/.mops/base@0.11.1/src/TrieSet.mo +++ /dev/null @@ -1,155 +0,0 @@ -/// Functional set -/// -/// Sets are partial maps from element type to unit type, -/// i.e., the partial map represents the set with its domain. - -// TODO-Matthew: -// --------------- -// -// - for now, we pass a hash value each time we pass an element value; -// in the future, we might avoid passing element hashes with each element in the API; -// related to: https://dfinity.atlassian.net/browse/AST-32 -// -// - similarly, we pass an equality function when we do some operations. -// in the future, we might avoid this via https://dfinity.atlassian.net/browse/AST-32 -import Trie "Trie"; -import Hash "Hash"; -import List "List"; -import Iter "Iter"; - -module { - - public type Hash = Hash.Hash; - public type Set = Trie.Trie; - type Key = Trie.Key; - type Trie = Trie.Trie; - - // helper for defining equal and sub, avoiding Trie.diff. - // TODO: add to Trie.mo? - private func keys(t : Trie) : Iter.Iter> { - object { - var stack = ?(t, null) : List.List>; - public func next() : ?Key { - switch stack { - case null { null }; - case (?(trie, stack2)) { - switch trie { - case (#empty) { - stack := stack2; - next() - }; - case (#leaf({ keyvals = null })) { - stack := stack2; - next() - }; - case (#leaf({ size = c; keyvals = ?((k, v), kvs) })) { - stack := ?(#leaf({ size = c - 1; keyvals = kvs }), stack2); - ?k - }; - case (#branch(br)) { - stack := ?(br.left, ?(br.right, stack2)); - next() - } - } - } - } - } - } - }; - - /// Empty set. - public func empty() : Set { Trie.empty() }; - - /// Put an element into the set. - public func put(s : Set, x : T, xh : Hash, eq : (T, T) -> Bool) : Set { - let (s2, _) = Trie.put(s, { key = x; hash = xh }, eq, ()); - s2 - }; - - /// Delete an element from the set. - public func delete(s : Set, x : T, xh : Hash, eq : (T, T) -> Bool) : Set { - let (s2, _) = Trie.remove(s, { key = x; hash = xh }, eq); - s2 - }; - - /// Test if two sets are equal. - public func equal(s1 : Set, s2 : Set, eq : (T, T) -> Bool) : Bool { - if (Trie.size(s1) != Trie.size(s2)) return false; - for (k in keys(s1)) { - if (Trie.find(s2, k, eq) == null) { - return false; - } - }; - return true; - }; - - /// The number of set elements, set's cardinality. - public func size(s : Set) : Nat { - Trie.size(s); - }; - - /// Test if `s` is the empty set. - public func isEmpty(s : Set) : Bool { - Trie.size(s) == 0; - }; - - /// Test if `s1` is a subset of `s2`. - public func isSubset(s1 : Set, s2 : Set, eq : (T, T) -> Bool) : Bool { - if (Trie.size(s1) > Trie.size(s2)) return false; - for (k in keys(s1)) { - if (Trie.find(s2, k, eq) == null) { - return false; - } - }; - return true; - }; - - /// @deprecated: use `TrieSet.contains()` - /// - /// Test if a set contains a given element. - public func mem(s : Set, x : T, xh : Hash, eq : (T, T) -> Bool) : Bool { - contains(s, x, xh, eq) - }; - - /// Test if a set contains a given element. - public func contains(s : Set, x : T, xh : Hash, eq : (T, T) -> Bool) : Bool { - switch (Trie.find(s, { key = x; hash = xh }, eq)) { - case null { false }; - case (?_) { true } - } - }; - - /// [Set union](https://en.wikipedia.org/wiki/Union_(set_theory)). - public func union(s1 : Set, s2 : Set, eq : (T, T) -> Bool) : Set { - let s3 = Trie.merge(s1, s2, eq); - s3 - }; - - /// [Set difference](https://en.wikipedia.org/wiki/Difference_(set_theory)). - public func diff(s1 : Set, s2 : Set, eq : (T, T) -> Bool) : Set { - let s3 = Trie.diff(s1, s2, eq); - s3 - }; - - /// [Set intersection](https://en.wikipedia.org/wiki/Intersection_(set_theory)). - public func intersect(s1 : Set, s2 : Set, eq : (T, T) -> Bool) : Set { - let noop : ((), ()) -> (()) = func(_ : (), _ : ()) : (()) = (); - let s3 = Trie.join(s1, s2, eq, noop); - s3 - }; - - //// Construct a set from an array. - public func fromArray(arr : [T], elemHash : T -> Hash, eq : (T, T) -> Bool) : Set { - var s = empty(); - for (elem in arr.vals()) { - s := put(s, elem, elemHash(elem), eq) - }; - s - }; - - //// Returns the set as an array. - public func toArray(s : Set) : [T] { - Trie.toArray(s, func(t : T, _ : ()) : T { t }) - } - -} diff --git a/.mops/core@2.3.1/LICENSE b/.mops/core@2.3.1/LICENSE deleted file mode 100644 index f593a1f..0000000 --- a/.mops/core@2.3.1/LICENSE +++ /dev/null @@ -1,208 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, and - distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by the - copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all other - entities that control, are controlled by, or are under common control with - that entity. For the purposes of this definition, "control" means (i) the - power, direct or indirect, to cause the direction or management of such - entity, whether by contract or otherwise, or (ii) ownership of fifty percent - (50%) or more of the outstanding shares, or (iii) beneficial ownership of - such entity. - - "You" (or "Your") shall mean an individual or Legal Entity exercising - permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation source, and - configuration files. - - "Object" form shall mean any form resulting from mechanical transformation - or translation of a Source form, including but not limited to compiled - object code, generated documentation, and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or Object form, - made available under the License, as indicated by a copyright notice that is - included in or attached to the work (an example is provided in the Appendix - below). - - "Derivative Works" shall mean any work, whether in Source or Object form, - that is based on (or derived from) the Work and for which the editorial - revisions, annotations, elaborations, or other modifications represent, as a - whole, an original work of authorship. For the purposes of this License, - Derivative Works shall not include works that remain separable from, or - merely link (or bind by name) to the interfaces of, the Work and Derivative - Works thereof. - - "Contribution" shall mean any work of authorship, including the original - version of the Work and any modifications or additions to that Work or - Derivative Works thereof, that is intentionally submitted to Licensor for - inclusion in the Work by the copyright owner or by an individual or Legal - Entity authorized to submit on behalf of the copyright owner. For the - purposes of this definition, "submitted" means any form of electronic, - verbal, or written communication sent to the Licensor or its - representatives, including but not limited to communication on electronic - mailing lists, source code control systems, and issue tracking systems that - are managed by, or on behalf of, the Licensor for the purpose of discussing - and improving the Work, but excluding communication that is conspicuously - marked or otherwise designated in writing by the copyright owner as "Not a - Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity on - behalf of whom a Contribution has been received by Licensor and subsequently - incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this - License, each Contributor hereby grants to You a perpetual, worldwide, - non-exclusive, no-charge, royalty-free, irrevocable copyright license to - reproduce, prepare Derivative Works of, publicly display, publicly perform, - sublicense, and distribute the Work and such Derivative Works in Source or - Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this - License, each Contributor hereby grants to You a perpetual, worldwide, - non-exclusive, no-charge, royalty-free, irrevocable (except as stated in - this section) patent license to make, have made, use, offer to sell, sell, - import, and otherwise transfer the Work, where such license applies only to - those patent claims licensable by such Contributor that are necessarily - infringed by their Contribution(s) alone or by combination of their - Contribution(s) with the Work to which such Contribution(s) was submitted. - If You institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work or a - Contribution incorporated within the Work constitutes direct or contributory - patent infringement, then any patent licenses granted to You under this - License for that Work shall terminate as of the date such litigation is - filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or - Derivative Works thereof in any medium, with or without modifications, and - in Source or Object form, provided that You meet the following conditions: - - a. You must give any other recipients of the Work or Derivative Works a - copy of this License; and - - b. You must cause any modified files to carry prominent notices stating - that You changed the files; and - - c. You must retain, in the Source form of any Derivative Works that You - distribute, all copyright, patent, trademark, and attribution notices - from the Source form of the Work, excluding those notices that do not - pertain to any part of the Derivative Works; and - - d. If the Work includes a "NOTICE" text file as part of its distribution, - then any Derivative Works that You distribute must include a readable - copy of the attribution notices contained within such NOTICE file, - excluding those notices that do not pertain to any part of the Derivative - Works, in at least one of the following places: within a NOTICE text file - distributed as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, within a - display generated by the Derivative Works, if and wherever such - third-party notices normally appear. The contents of the NOTICE file are - for informational purposes only and do not modify the License. You may - add Your own attribution notices within Derivative Works that You - distribute, alongside or as an addendum to the NOTICE text from the Work, - provided that such additional attribution notices cannot be construed as - modifying the License. - - You may add Your own copyright statement to Your modifications and may - provide additional or different license terms and conditions for use, - reproduction, or distribution of Your modifications, or for any such - Derivative Works as a whole, provided Your use, reproduction, and - distribution of the Work otherwise complies with the conditions stated in - this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any - Contribution intentionally submitted for inclusion in the Work by You to the - Licensor shall be under the terms and conditions of this License, without - any additional terms or conditions. Notwithstanding the above, nothing - herein shall supersede or modify the terms of any separate license agreement - you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, - trademarks, service marks, or product names of the Licensor, except as - required for reasonable and customary use in describing the origin of the - Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in - writing, Licensor provides the Work (and each Contributor provides its - Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied, including, without limitation, any - warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or - FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining - the appropriateness of using or redistributing the Work and assume any risks - associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in - tort (including negligence), contract, or otherwise, unless required by - applicable law (such as deliberate and grossly negligent acts) or agreed to - in writing, shall any Contributor be liable to You for damages, including - any direct, indirect, special, incidental, or consequential damages of any - character arising as a result of this License or out of the use or inability - to use the Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all other - commercial damages or losses), even if such Contributor has been advised of - the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or - Derivative Works thereof, You may choose to offer, and charge a fee for, - acceptance of support, warranty, indemnity, or other liability obligations - and/or rights consistent with this License. However, in accepting such - obligations, You may act only on Your own behalf and on Your sole - responsibility, not on behalf of any other Contributor, and only if You - agree to indemnify, defend, and hold each Contributor harmless for any - liability incurred by, or claims asserted against, such Contributor by - reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -LLVM EXCEPTIONS TO THE APACHE 2.0 LICENSE - -As an exception, if, as a result of your compiling your source code, portions -of this Software are embedded into an Object form of such source code, you may -redistribute such embedded portions in such Object form without complying with -the conditions of Sections 4(a), 4(b) and 4(d) of the License. - -In addition, if you combine or link compiled forms of this Software with -software that is licensed under the GPLv2 ("Combined Software") and if a court -of competent jurisdiction determines that the patent provision (Section 3), the -indemnity provision (Section 9) or other Section of the License conflicts with -the conditions of the GPLv2, you may retroactively and prospectively choose to -deem waived or otherwise exclude such Section(s) of the License, but only in -their entirety and only with respect to the Combined Software. - -END OF LLVM EXCEPTIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification -within third-party archives. - -Copyright 2025 DFINITY Stiftung - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. - -END OF APPENDIX diff --git a/.mops/core@2.3.1/NOTICE b/.mops/core@2.3.1/NOTICE deleted file mode 100644 index a25e095..0000000 --- a/.mops/core@2.3.1/NOTICE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright 2025 DFINITY Stiftung - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. - -This product contains modified software originally developed by MR Research AG, -used with permission: - -* https://github.com/research-ag/vector -* https://github.com/research-ag/prng diff --git a/.mops/core@2.3.1/README.md b/.mops/core@2.3.1/README.md deleted file mode 100644 index f7a869c..0000000 --- a/.mops/core@2.3.1/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# `core` - -* 📦 [Mops Package](https://mops.one/core) -* ✨ [Documentation](https://internetcomputer.org/docs/motoko/core) - ---- - -The `core` package is the official standard library for the [Motoko](https://github.com/dfinity/motoko) programming language. - -This replaces the original `base` library, which is available [here](https://github.com/dfinity/motoko-base). - -An official [migration guide](https://internetcomputer.org/docs/motoko/base-core-migration) is available for upgrading projects from `base` to `core`. - -## Quick Start - -1. Install the [Mops](https://docs.mops.one/quick-start) package manager -2. Open a terminal in your project directory -3. Run `mops add core` - -This adds the following dependency to your `mops.toml` config file: - -```toml -[dependencies] -core = "2.3.1" -``` - -## Contributing - -This repository is currently closed to external contributions. Please feel free to report a bug, ask a question, or request a feature on the project's [GitHub issues](https://github.com/dfinity/motoko-core/issues) page. - -Interface design and code style guidelines for the repository can be found [here](https://github.com/dfinity/motoko-core/blob/main/Styleguide.md). - -### Dev Environment - -> Make sure that [Node.js](https://nodejs.org/en/) `>= 22.x` is installed on your system. - -Run the following commands to configure your local development branch: - -```sh -# First-time setup -git clone https://github.com/dfinity/motoko-core -cd motoko-core -npm ci -npx ic-mops toolchain init -``` - -Below is a quick reference for commonly-used scripts during development: - -```sh -npm test # Run all tests -npm run format # Format Motoko files -npm run validate:api # Update the public API lockfile -npm run validate:docs Array # Run code snippets in `src/Array.mo` -``` - -All available scripts can be found in the project's [`package.json`](https://github.com/dfinity/motoko-core/blob/main/package.json) file. - -### Major Contributors - -Big thanks to the following community contributors: - -* [MR Research AG (A. Stepanov, T. Hanke)](https://github.com/research-ag): [`vector`](https://github.com/research-ag/vector), [`prng`](https://github.com/research-ag/prng) -* [Byron Becker](https://github.com/ByronBecker): [`StableHeapBTreeMap`](https://github.com/canscale/StableHeapBTreeMap) -* [Zen Voich](https://github.com/ZenVoich): [`test`](https://github.com/ZenVoich/test) diff --git a/.mops/core@2.3.1/mops.toml b/.mops/core@2.3.1/mops.toml deleted file mode 100644 index 48b60c4..0000000 --- a/.mops/core@2.3.1/mops.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "core" -version = "2.3.1" -description = "The Motoko standard library" -repository = "https://github.com/caffeinelabs/motoko-core" -keywords = [ - "core", - "base", - "data-structure", - "stable-memory", - "persistent" -] -license = "Apache-2.0" - -[dev-dependencies] -test = "2.1.1" -bench = "1.0.0" -fuzz = "1.0.0" -matchers = "2.1.0" -base-0-14-13 = "https://github.com/dfinity/motoko-base#moc-0.14.13@794174a307975c225cfb26b57f73e38a841c0415" - -[requirements] -moc = "1.0.0" - -[toolchain] -moc = "1.3.0" -wasmtime = "35.0.0" diff --git a/.mops/core@2.3.1/src/Array.mo b/.mops/core@2.3.1/src/Array.mo deleted file mode 100644 index d833ced..0000000 --- a/.mops/core@2.3.1/src/Array.mo +++ /dev/null @@ -1,1182 +0,0 @@ -/// Provides extended utility functions on immutable Arrays (values of type `[T]`). -/// -/// Note the difference between mutable (`[var T]`) and immutable (`[T]`) arrays. -/// Mutable arrays allow their elements to be modified after creation, while -/// immutable arrays are fixed once created. -/// -/// WARNING: If you are looking for a list that can grow and shrink in size, -/// it is recommended you use `List` for those purposes. -/// Arrays must be created with a fixed size. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Array "mo:core/Array"; -/// ``` - -import Order "Order"; -import VarArray "VarArray"; -import Option "Option"; -import Types "Types"; -import Prim "mo:⛔"; - -module { - - /// Creates an empty array (equivalent to `[]`). - /// - /// ```motoko include=import - /// let array = Array.empty(); - /// assert array == []; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func empty() : [T] = []; - - /// Creates an array containing `item` repeated `size` times. - /// - /// ```motoko include=import - /// let array = Array.repeat("Echo", 3); - /// assert array == ["Echo", "Echo", "Echo"]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func repeat(item : T, size : Nat) : [T] = Prim.Array_tabulate(size, func _ = item); - - /// Creates an immutable array of size `size`. Each element at index i - /// is created by applying `generator` to i. - /// - /// ```motoko include=import - /// let array : [Nat] = Array.tabulate(4, func i = i * 2); - /// assert array == [0, 2, 4, 6]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `generator` runs in O(1) time and space. - public let tabulate : (size : Nat, generator : Nat -> T) -> [T] = Prim.Array_tabulate; - - /// Transforms a mutable array into an immutable array. - /// - /// ```motoko include=import - /// let varArray = [var 0, 1, 2]; - /// varArray[2] := 3; - /// let array = Array.fromVarArray(varArray); - /// assert array == [0, 1, 3]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// @deprecated M0235 - public func fromVarArray(varArray : [var T]) : [T] = Prim.Array_tabulate(varArray.size(), func i = varArray[i]); - - /// Transforms an immutable array into a mutable array. - /// - /// ```motoko include=import - /// import VarArray "mo:core/VarArray"; - /// import Nat "mo:core/Nat"; - /// - /// let array = [0, 1, 2]; - /// let varArray = Array.toVarArray(array); - /// varArray[2] := 3; - /// assert VarArray.equal(varArray, [var 0, 1, 3], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func toVarArray(self : [T]) : [var T] { - let size = self.size(); - if (size == 0) { - return [var] - }; - let newArray = Prim.Array_init(size, self[0]); - var i = 0; - while (i < size) { - newArray[i] := self[i]; - i += 1 - }; - newArray - }; - - /// Tests if two arrays contain equal values (i.e. they represent the same - /// list of elements). Uses `equal` to compare elements in the arrays. - /// - /// ```motoko include=import - /// // Use the equal function from the Nat module to compare Nats - /// import {equal} "mo:core/Nat"; - /// - /// let array1 = [0, 1, 2, 3]; - /// let array2 = [0, 1, 2, 3]; - /// assert Array.equal(array1, array2, equal); - /// ``` - /// - /// Runtime: O(size1 + size2) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func equal(self : [T], other : [T], equal : (implicit : (T, T) -> Bool)) : Bool { - let size1 = self.size(); - let size2 = other.size(); - if (size1 != size2) { - return false - }; - var i = 0; - while (i < size1) { - if (not equal(self[i], other[i])) { - return false - }; - i += 1 - }; - true - }; - - /// Returns the first value in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let array = [1, 9, 4, 8]; - /// let found = Array.find(array, func x = x > 8); - /// assert found == ?9; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func find(self : [T], predicate : T -> Bool) : ?T { - for (element in self.vals()) { - if (predicate(element)) { - return ?element - } - }; - null - }; - - /// Returns the first index in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let array = ['A', 'B', 'C', 'D']; - /// let found = Array.findIndex(array, func(x) { x == 'C' }); - /// assert found == ?2; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func findIndex(self : [T], predicate : T -> Bool) : ?Nat { - for ((index, element) in enumerate(self)) { - if (predicate(element)) { - return ?index - } - }; - null - }; - - /// Create a new array by concatenating the values of `array1` and `array2`. - /// Note that `Array.concat` copies its arguments and has linear complexity. - /// - /// ```motoko include=import - /// let array1 = [1, 2, 3]; - /// let array2 = [4, 5, 6]; - /// let result = Array.concat(array1, array2); - /// assert result == [1, 2, 3, 4, 5, 6]; - /// ``` - /// Runtime: O(size1 + size2) - /// - /// Space: O(size1 + size2) - public func concat(self : [T], other : [T]) : [T] { - let size1 = self.size(); - let size2 = other.size(); - Prim.Array_tabulate( - size1 + size2, - func i { - if (i < size1) { - self[i] - } else { - other[i - size1] - } - } - ) - }; - - /// Sorts the elements in the array according to `compare`. - /// Sort is deterministic and stable. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [4, 2, 6]; - /// let sorted = Array.sort(array, Nat.compare); - /// assert sorted == [2, 4, 6]; - /// ``` - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sort(self : [T], compare : (implicit : (T, T) -> Order.Order)) : [T] { - let varArray : [var T] = toVarArray(self); - VarArray.sortInPlace(varArray, compare); - fromVarArray(varArray) - }; - - /// Creates a new array by reversing the order of elements in `array`. - /// - /// ```motoko include=import - /// let array = [10, 11, 12]; - /// let reversed = Array.reverse(array); - /// assert reversed == [12, 11, 10]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func reverse(self : [T]) : [T] { - let size = self.size(); - Prim.Array_tabulate(size, func i = self[size - i - 1]) - }; - - /// Calls `f` with each element in `array`. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// var sum = 0; - /// let array = [0, 1, 2, 3]; - /// Array.forEach(array, func(x) { - /// sum += x; - /// }); - /// assert sum == 6; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEach(self : [T], f : T -> ()) { - for (item in self.vals()) { - f(item) - } - }; - - /// Creates a new array by applying `f` to each element in `array`. `f` "maps" - /// each element it is applied to of type `X` to an element of type `Y`. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// let array1 = [0, 1, 2, 3]; - /// let array2 = Array.map(array1, func x = x * 2); - /// assert array2 == [0, 2, 4, 6]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func map(self : [T], f : T -> R) : [R] = Prim.Array_tabulate(self.size(), func i = f(self[i])); - - /// Creates a new array by applying `predicate` to every element - /// in `array`, retaining the elements for which `predicate` returns true. - /// - /// ```motoko include=import - /// let array = [4, 2, 6, 1, 5]; - /// let evenElements = Array.filter(array, func x = x % 2 == 0); - /// assert evenElements == [4, 2, 6]; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func filter(self : [T], f : T -> Bool) : [T] { - var count = 0; - let keep = Prim.Array_tabulate( - self.size(), - func i { - if (f(self[i])) { - count += 1; - true - } else { - false - } - } - ); - var nextKeep = 0; - Prim.Array_tabulate( - count, - func _ { - while (not keep[nextKeep]) { - nextKeep += 1 - }; - nextKeep += 1; - self[nextKeep - 1] - } - ) - }; - - /// Creates a new array by applying `f` to each element in `array`, - /// and keeping all non-null elements. The ordering is retained. - /// - /// ```motoko include=import - /// import {toText} "mo:core/Nat"; - /// - /// let array = [4, 2, 0, 1]; - /// let newArray = - /// Array.filterMap( // mapping from Nat to Text values - /// array, - /// func x = if (x == 0) { null } else { ?toText(100 / x) } // can't divide by 0, so return null - /// ); - /// assert newArray == ["25", "50", "100"]; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func filterMap(self : [T], f : T -> ?R) : [R] { - var count = 0; - let options = Prim.Array_tabulate( - self.size(), - func i { - let result = f(self[i]); - switch (result) { - case (?element) { - count += 1; - result - }; - case null { - null - } - } - } - ); - - var nextSome = 0; - Prim.Array_tabulate( - count, - func _ { - while (Option.isNull(options[nextSome])) { - nextSome += 1 - }; - nextSome += 1; - switch (options[nextSome - 1]) { - case (?element) element; - case null { - Prim.trap "Array.filterMap(): malformed array" - } - } - } - ) - }; - - /// Creates a new array by applying `f` to each element in `array`. - /// If any invocation of `f` produces an `#err`, returns an `#err`. Otherwise - /// returns an `#ok` containing the new array. - /// - /// ```motoko include=import - /// let array = [4, 3, 2, 1, 0]; - /// // divide 100 by every element in the array - /// let result = Array.mapResult(array, func x { - /// if (x > 0) { - /// #ok(100 / x) - /// } else { - /// #err "Cannot divide by zero" - /// } - /// }); - /// assert result == #err "Cannot divide by zero"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - /// @deprecated M0235 - public func mapResult(self : [T], f : T -> Types.Result) : Types.Result<[R], E> { - let size = self.size(); - - var error : ?Types.Result<[R], E> = null; - let results = Prim.Array_tabulate( - size, - func i { - switch (f(self[i])) { - case (#ok element) { - ?element - }; - case (#err e) { - switch (error) { - case null { - // only take the first error - error := ?(#err e) - }; - case _ {} - }; - null - } - } - } - ); - - switch error { - case null { - // unpack the option - #ok( - map( - results, - func element { - switch element { - case (?element) { - element - }; - case null { - Prim.trap "Array.mapResult(): malformed array" - } - } - } - ) - ) - }; - case (?error) { - error - } - } - }; - - /// Creates a new array by applying `f` to each element in `array` and its index. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// let array = [10, 10, 10, 10]; - /// let newArray = Array.mapEntries(array, func (x, i) = i * x); - /// assert newArray == [0, 10, 20, 30]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapEntries(self : [T], f : (T, Nat) -> R) : [R] = Prim.Array_tabulate(self.size(), func i = f(self[i], i)); - - /// Creates a new array by applying `k` to each element in `array`, - /// and concatenating the resulting arrays in order. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4]; - /// let newArray = Array.flatMap(array, func x = [x, -x].values()); - /// assert newArray == [1, -1, 2, -2, 3, -3, 4, -4]; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `k` runs in O(1) time and space. - public func flatMap(self : [T], k : T -> Types.Iter) : [R] { - var flatSize = 0; - let arrays = Prim.Array_tabulate<[R]>( - self.size(), - func i { - let subArray = fromIter(k(self[i])); - flatSize += subArray.size(); - subArray - } - ); - - // could replace with a call to flatten, - // but it would require an extra pass (to compute `flatSize`) - var outer = 0; - var inner = 0; - Prim.Array_tabulate( - flatSize, - func _ { - while (inner == arrays[outer].size()) { - inner := 0; - outer += 1 - }; - let element = arrays[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// ```motoko include=import - /// import {add} "mo:core/Nat"; - /// - /// let array = [4, 2, 0, 1]; - /// let sum = - /// Array.foldLeft( - /// array, - /// 0, // start the sum at 0 - /// func(sumSoFar, x) = sumSoFar + x // this entire function can be replaced with `add`! - /// ); - /// assert sum == 7; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(self : [T], base : A, combine : (A, T) -> A) : A { - var acc = base; - for (element in self.values()) { - acc := combine(acc, element) - }; - acc - }; - - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// ```motoko include=import - /// import {toText} "mo:core/Nat"; - /// - /// let array = [1, 9, 4, 8]; - /// let bookTitle = Array.foldRight(array, "", func(x, acc) = toText(x) # acc); - /// assert bookTitle == "1948"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldRight(self : [T], base : A, combine : (T, A) -> A) : A { - var acc = base; - let size = self.size(); - var i = size; - while (i > 0) { - i -= 1; - acc := combine(self[i], acc) - }; - acc - }; - - /// Combines an iterator of arrays into a single array. Retains the original - /// ordering of the elements. - /// - /// Consider using `Array.flatten()` for better performance. - /// - /// ```motoko include=import - /// let arrays = [[0, 1, 2], [2, 3], [], [4]]; - /// let joinedArray = Array.join(arrays.values()); - /// assert joinedArray == [0, 1, 2, 2, 3, 4]; - /// ``` - /// - /// Runtime: O(number of elements in array) - /// - /// Space: O(number of elements in array) - public func join(self : Types.Iter<[T]>) : [T] { - flatten(fromIter(self)) - }; - - /// Combines an array of arrays into a single array. Retains the original - /// ordering of the elements. - /// - /// This has better performance compared to `Array.join()`. - /// - /// ```motoko include=import - /// let arrays = [[0, 1, 2], [2, 3], [], [4]]; - /// let flatArray = Array.flatten(arrays); - /// assert flatArray == [0, 1, 2, 2, 3, 4]; - /// ``` - /// - /// Runtime: O(number of elements in array) - /// - /// Space: O(number of elements in array) - public func flatten(self : [[T]]) : [T] { - var flatSize = 0; - for (subArray in self.vals()) { - flatSize += subArray.size() - }; - - var outer = 0; - var inner = 0; - Prim.Array_tabulate( - flatSize, - func _ { - while (inner == self[outer].size()) { - inner := 0; - outer += 1 - }; - let element = self[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Create an array containing a single value. - /// - /// ```motoko include=import - /// let array = Array.singleton(2); - /// assert array == [2]; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func singleton(element : T) : [T] = [element]; - - /// Returns the size of an array. Equivalent to `array.size()`. - public func size(self : [T]) : Nat = self.size(); - - /// Returns whether an array is empty, i.e. contains zero elements. - public func isEmpty(self : [T]) : Bool = self.size() == 0; - - /// Converts an iterator to an array. - /// @deprecated M0235 - public func fromIter(iter : Types.Iter) : [T] { - var list : Types.Pure.List = null; - var size = 0; - label l loop { - switch (iter.next()) { - case (?element) { - list := ?(element, list); - size += 1 - }; - case null { break l } - } - }; - if (size == 0) { return [] }; - let array = Prim.Array_init( - size, - switch list { - case (?(h, _)) h; - case null { - Prim.trap("Array.fromIter(): unreachable") - } - } - ); - var i = size : Nat; - while (i > 0) { - i -= 1; - switch list { - case (?(h, t)) { - array[i] := h; - list := t - }; - case null { - Prim.trap("Array.fromIter(): unreachable") - } - } - }; - Prim.Array_tabulate(size, func i = array[i]) - }; - - /// Returns an iterator (`Iter`) over the indices of `array`. - /// An iterator provides a single method `next()`, which returns - /// indices in order, or `null` when out of index to iterate over. - /// - /// Note: You can also use `array.keys()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// let array = [10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.keys()) { - /// sum += element; - /// }; - /// assert sum == 3; // 0 + 1 + 2 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func keys(self : [T]) : Types.Iter = self.keys(); - - /// Iterator provides a single method `next()`, which returns - /// elements in order, or `null` when out of elements to iterate over. - /// - /// Note: You can also use `array.values()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// let array = [10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.values()) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func values(self : [T]) : Types.Iter = self.values(); - - /// Iterator provides a single method `next()`, which returns - /// pairs of (index, element) in order, or `null` when out of elements to iterate over. - /// - /// ```motoko include=import - /// let array = [10, 11, 12]; - /// - /// var sum = 0; - /// for ((index, element) in Array.enumerate(array)) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func enumerate(self : [T]) : Types.Iter<(Nat, T)> = object { - let size = self.size(); - var index = 0; - public func next() : ?(Nat, T) { - if (index >= size) { - return null - }; - let i = index; - index += 1; - ?(i, self[i]) - } - }; - - /// Returns true if all elements in `array` satisfy the predicate function. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4]; - /// assert Array.all(array, func x = x > 0); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func all(self : [T], predicate : T -> Bool) : Bool { - for (element in self.values()) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Returns true if any element in `array` satisfies the predicate function. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4]; - /// assert Array.any(array, func x = x > 3); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func any(self : [T], predicate : T -> Bool) : Bool { - for (element in self.values()) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Returns the index of the first `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.indexOf(array, Char.equal, 'c') == ?0; - /// assert Array.indexOf(array, Char.equal, 'f') == ?2; - /// assert Array.indexOf(array, Char.equal, 'g') == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func indexOf(self : [T], equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = nextIndexOf(self, equal, element, 0); - - /// Returns the index of the next occurence of `element` in the `array` starting from the `from` index (inclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.nextIndexOf(array, Char.equal, 'c', 0) == ?0; - /// assert Array.nextIndexOf(array, Char.equal, 'f', 0) == ?2; - /// assert Array.nextIndexOf(array, Char.equal, 'f', 2) == ?2; - /// assert Array.nextIndexOf(array, Char.equal, 'f', 3) == ?3; - /// assert Array.nextIndexOf(array, Char.equal, 'f', 4) == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func nextIndexOf(self : [T], equal : (implicit : (T, T) -> Bool), element : T, fromInclusive : Nat) : ?Nat { - var index = fromInclusive; - let size = self.size(); - while (index < size) { - if (equal(self[index], element)) { - return ?index - } else { - index += 1 - } - }; - null - }; - - /// Returns the index of the last `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.lastIndexOf(array, Char.equal, 'c') == ?0; - /// assert Array.lastIndexOf(array, Char.equal, 'f') == ?3; - /// assert Array.lastIndexOf(array, Char.equal, 'e') == ?5; - /// assert Array.lastIndexOf(array, Char.equal, 'g') == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func lastIndexOf(self : [T], equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = prevIndexOf(self, equal, element, self.size()); - - /// Returns the index of the previous occurence of `element` in the `array` starting from the `from` index (exclusive). - /// - /// Negative indices are relative to the end of the array. For example, `-1` corresponds to the last element in the array. - /// - /// If the indices are out of bounds, they are clamped to the array bounds. - /// If the first index is greater than the second, the function returns an empty iterator. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.prevIndexOf(array, Char.equal, 'c', array.size()) == ?0; - /// assert Array.prevIndexOf(array, Char.equal, 'e', array.size()) == ?5; - /// assert Array.prevIndexOf(array, Char.equal, 'e', 5) == ?4; - /// assert Array.prevIndexOf(array, Char.equal, 'e', 4) == null; - /// ``` - /// - /// Runtime: O(array.size()); - /// Space: O(1); - public func prevIndexOf(self : [T], equal : (implicit : (T, T) -> Bool), element : T, fromExclusive : Nat) : ?Nat { - var i = fromExclusive; - while (i > 0) { - i -= 1; - if (equal(self[i], element)) { - return ?i - } - }; - null - }; - - /// Returns true if the `array` contains `element` using the provided `equal` function. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.contains(array, Char.equal, 'f'); - /// assert not Array.contains(array, Char.equal, 'g'); - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func contains(self : [T], equal : (implicit : (T, T) -> Bool), element : T) : Bool { - for (item in self.vals()) { - if (equal(item, element)) { - return true - } - }; - false - }; - - /// Returns an iterator over a slice of `array` starting at `fromInclusive` up to (but not including) `toExclusive`. - /// - /// Negative indices are relative to the end of the array. For example, `-1` corresponds to the last element in the array. - /// - /// If the indices are out of bounds, they are clamped to the array bounds. - /// If the first index is greater than the second, the function returns an empty iterator. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4, 5]; - /// let iter1 = Array.range(array, 3, array.size()); - /// assert iter1.next() == ?4; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// let iter2 = Array.range(array, 3, -1); - /// assert iter2.next() == ?4; - /// assert iter2.next() == null; - /// - /// let iter3 = Array.range(array, 0, 0); - /// assert iter3.next() == null; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func range(self : [T], fromInclusive : Int, toExclusive : Int) : Types.Iter { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - object { - var pos = start; - public func next() : ?T { - if (pos >= end) { - null - } else { - let elem = self[pos]; - pos += 1; - ?elem - } - } - } - }; - - /// Returns a new array containing elements from `array` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4, 5]; - /// - /// let slice1 = Array.sliceToArray(array, 1, 4); - /// assert slice1 == [2, 3, 4]; - /// - /// let slice2 = Array.sliceToArray(array, 1, -1); - /// assert slice2 == [2, 3, 4]; - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToArray(self : [T], fromInclusive : Int, toExclusive : Int) : [T] { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - if (start >= end) { - return [] - }; - Prim.Array_tabulate(end - start, func i = self[start + i]) - }; - - /// Returns a new mutable array containing elements from `array` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// import VarArray "mo:core/VarArray"; - /// import Nat "mo:core/Nat"; - /// - /// let array = [1, 2, 3, 4, 5]; - /// - /// let slice1 = Array.sliceToVarArray(array, 1, 4); - /// assert VarArray.equal(slice1, [var 2, 3, 4], Nat.equal); - /// - /// let slice2 = Array.sliceToVarArray(array, 1, -1); - /// assert VarArray.equal(slice2, [var 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToVarArray(self : [T], fromInclusive : Int, toExclusive : Int) : [var T] { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - if (start >= end) { - return [var] - }; - Prim.Array_tabulateVar(end - start, func i = self[start + i]) - }; - - /// Converts the array to its textual representation using `f` to convert each element to `Text`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [1, 2, 3]; - /// let text = Array.toText(array, Nat.toText); - /// assert text == "[1, 2, 3]"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func toText(self : [T], f : (implicit : (toText : T -> Text))) : Text { - let size = self.size(); - if (size == 0) { return "[]" }; - var text = "["; - var i = 0; - while (i < size) { - if (i != 0) { - text #= ", " - }; - text #= f(self[i]); - i += 1 - }; - text #= "]"; - text - }; - - /// Compares two arrays using the provided comparison function for elements. - /// Returns #less, #equal, or #greater if `array1` is less than, equal to, - /// or greater than `array2` respectively. - /// - /// If arrays have different sizes but all elements up to the shorter length are equal, - /// the shorter array is considered #less than the longer array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array1 = [1, 2, 3]; - /// let array2 = [1, 2, 4]; - /// assert Array.compare(array1, array2, Nat.compare) == #less; - /// ``` - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array3 = [1, 2]; - /// let array4 = [1, 2, 3]; - /// assert Array.compare(array3, array4, Nat.compare) == #less; - /// ``` - /// - /// Runtime: O(min(size1, size2)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func compare(self : [T], other : [T], compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let size1 = self.size(); - let size2 = other.size(); - var i = 0; - let minSize = if (size1 < size2) { size1 } else { size2 }; - while (i < minSize) { - switch (compare(self[i], other[i])) { - case (#less) { return #less }; - case (#greater) { return #greater }; - case (#equal) { i += 1 } - } - }; - if (size1 < size2) { #less } else if (size1 > size2) { #greater } else { - #equal - } - }; - - /// Performs binary search on a sorted array to find the index of the `element`. - /// - /// Returns `#found(index)` if the element is found, or `#insertionIndex(index)` with the index - /// where the element would be inserted according to the ordering if not found. - /// - /// If there are multiple equal elements, no guarantee is made about which index is returned. - /// The array must be sorted in ascending order according to the `compare` function. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let sorted = [1, 3, 5, 7, 9, 11]; - /// assert Array.binarySearch(sorted, Nat.compare, 5) == #found(2); - /// assert Array.binarySearch(sorted, Nat.compare, 6) == #insertionIndex(3); - /// ``` - /// - /// Runtime: O(log(size)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func binarySearch(self : [T], compare : (implicit : (T, T) -> Order.Order), element : T) : { - #found : Nat; - #insertionIndex : Nat - } { - var left = 0; - var right = self.size(); - while (left < right) { - let mid = (left + right) / 2; - switch (compare(self[mid], element)) { - case (#less) left := mid + 1; - case (#greater) right := mid; - case (#equal) return #found mid - } - }; - #insertionIndex left - }; - - /// Checks whether the `array` is sorted according to the `compare` function. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [1, 2, 3]; - /// assert Array.isSorted(array, Nat.compare); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func isSorted(self : [T], compare : (implicit : (T, T) -> Order.Order)) : Bool { - let size = self.size(); - if (size <= 1) return true; - var i = 1; - while (i < size) { - switch (compare(self[i - 1], self[i])) { - case (#greater) return false; - case _ { i += 1 } - } - }; - true - } -} diff --git a/.mops/core@2.3.1/src/Base64.mo b/.mops/core@2.3.1/src/Base64.mo deleted file mode 100644 index 6bb35d5..0000000 --- a/.mops/core@2.3.1/src/Base64.mo +++ /dev/null @@ -1,79 +0,0 @@ -/// Module for Base64 encoding of byte sequences. -/// -/// Base64 encoding converts binary data to an ASCII string using 64 printable -/// characters, as specified in [RFC 4648](https://www.rfc-editor.org/rfc/rfc4648). -/// It is widely used for HTTP Basic Authentication, encoding binary data in -/// JSON payloads, and data URIs. -/// -/// This module uses the standard Base64 alphabet (`A–Z`, `a–z`, `0–9`, `+`, `/`) -/// and pads output to a multiple of 4 characters using `=`. -/// -/// Authored by Claude Sonnet (claude-sonnet-4-6) for use in generated -/// Motoko API clients. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Base64 "mo:core/Base64"; -/// ``` - -import Nat8 "Nat8"; -import Nat32 "Nat32"; -import Text "Text"; -import Blob "Blob"; - -module { - - // Standard Base64 alphabet (RFC 4648 §4). - // prettier-ignore - private let alphabet : [Char] = [ - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - ]; - - /// Encodes a `Blob` as a Base64 `Text` string (RFC 4648 §4). - /// - /// Output length is always a multiple of 4, padded with `=` as needed. - /// An empty `Blob` encodes to an empty `Text`. - /// - /// Example: - /// ```motoko include=import - /// assert Base64.encode("" : Blob) == ""; - /// assert Base64.encode("f" : Blob) == "Zg=="; - /// assert Base64.encode("fo" : Blob) == "Zm8="; - /// assert Base64.encode("foo" : Blob) == "Zm9v"; - /// assert Base64.encode("foobar" : Blob) == "Zm9vYmFy"; - /// ``` - /// - /// Typical use — encoding HTTP Basic Auth credentials: - /// ```motoko include=import - /// // Encodes "user:pass" → "dXNlcjpwYXNz" - /// let credentials = "user:pass" : Blob; - /// let header = "Basic " # Base64.encode(credentials); - /// assert header == "Basic dXNlcjpwYXNz"; - /// ``` - public func encode(data : Blob) : Text { - let bytes = Blob.toArray(data); - var result = ""; - var i = 0; - while (i < bytes.size()) { - let b1 = bytes[i]; - let b2 : Nat8 = if (i + 1 < bytes.size()) bytes[i + 1] else 0; - let b3 : Nat8 = if (i + 2 < bytes.size()) bytes[i + 2] else 0; - - let n = (Nat32.fromNat(Nat8.toNat(b1)) << 16) | (Nat32.fromNat(Nat8.toNat(b2)) << 8) | Nat32.fromNat(Nat8.toNat(b3)); - - let c1 = Text.fromChar(alphabet[Nat32.toNat((n >> 18) & 0x3F)]); - let c2 = Text.fromChar(alphabet[Nat32.toNat((n >> 12) & 0x3F)]); - let c3 = if (i + 1 < bytes.size()) Text.fromChar(alphabet[Nat32.toNat((n >> 6) & 0x3F)]) else "="; - let c4 = if (i + 2 < bytes.size()) Text.fromChar(alphabet[Nat32.toNat(n & 0x3F)]) else "="; - - result #= c1 # c2 # c3 # c4; - i += 3 - }; - result - }; - -} diff --git a/.mops/core@2.3.1/src/Blob.mo b/.mops/core@2.3.1/src/Blob.mo deleted file mode 100644 index 64de595..0000000 --- a/.mops/core@2.3.1/src/Blob.mo +++ /dev/null @@ -1,242 +0,0 @@ -/// Module for working with Blobs (immutable sequences of bytes). -/// -/// Blobs represent sequences of bytes. They are immutable, iterable, but not indexable and can be empty. -/// -/// Byte sequences are also often represented as `[Nat8]`, i.e. an array of bytes, but this representation is currently much less compact than `Blob`, taking 4 physical bytes to represent each logical byte in the sequence. -/// If you would like to manipulate Blobs, it is recommended that you convert -/// Blobs to `[var Nat8]` or `Buffer`, do the manipulation, then convert back. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Blob "mo:core/Blob"; -/// ``` -/// -/// Some built in features not listed in this module: -/// -/// * You can create a `Blob` literal from a `Text` literal, provided the context expects an expression of type `Blob`. -/// * `b.size() : Nat` returns the number of bytes in the blob `b`; -/// * `b.values() : Iter.Iter` returns an iterator to enumerate the bytes of the blob `b`. -/// -/// For example: -/// ```motoko include=import -/// import Debug "mo:core/Debug"; -/// import Nat8 "mo:core/Nat8"; -/// -/// let blob = "\00\00\00\ff" : Blob; // blob literals, where each byte is delimited by a back-slash and represented in hex -/// let blob2 = "charsもあり" : Blob; // you can also use characters in the literals -/// let numBytes = blob.size(); -/// assert numBytes == 4; // returns the number of bytes in the Blob -/// for (byte in blob.values()) { // iterator over the Blob -/// Debug.print(Nat8.toText(byte)) -/// } -/// ``` - -import Types "Types"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - public type Blob = Prim.Types.Blob; - - /// Returns an empty `Blob` (equivalent to `""`). - /// - /// Example: - /// ```motoko include=import - /// let emptyBlob = Blob.empty(); - /// assert emptyBlob.size() == 0; - /// ``` - public func empty() : Blob = ""; - - /// Returns whether the given `Blob` is empty (has a size of zero). - /// - /// ```motoko include=import - /// let blob1 = "" : Blob; - /// let blob2 = "\FF\00" : Blob; - /// assert Blob.isEmpty(blob1); - /// assert not Blob.isEmpty(blob2); - /// ``` - public func isEmpty(self : Blob) : Bool = self == ""; - - /// Returns the number of bytes in the given `Blob`. - /// This is equivalent to `blob.size()`. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\FF\00\AA" : Blob; - /// assert Blob.size(blob) == 3; - /// assert blob.size() == 3; - /// ``` - public func size(self : Blob) : Nat = self.size(); - - /// Creates a `Blob` from an array of bytes (`[Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// let bytes : [Nat8] = [0, 255, 0]; - /// let blob = Blob.fromArray(bytes); - /// assert blob == "\00\FF\00"; - /// ``` - public let fromArray : (bytes : [Nat8]) -> Blob = Prim.arrayToBlob; - - /// Creates a `Blob` from a mutable array of bytes (`[var Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// let bytes : [var Nat8] = [var 0, 255, 0]; - /// let blob = Blob.fromVarArray(bytes); - /// assert blob == "\00\FF\00"; - /// ``` - public let fromVarArray : (bytes : [var Nat8]) -> Blob = Prim.arrayMutToBlob; - - /// Converts a `Blob` to an array of bytes (`[Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\00\FF\00" : Blob; - /// let bytes = Blob.toArray(blob); - /// assert bytes == [0, 255, 0]; - /// ``` - public let toArray : (self : Blob) -> [Nat8] = Prim.blobToArray; - - /// Converts a `Blob` to a mutable array of bytes (`[var Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// import Nat8 "mo:core/Nat8"; - /// import VarArray "mo:core/VarArray"; - /// - /// let blob = "\00\FF\00" : Blob; - /// let bytes = Blob.toVarArray(blob); - /// assert VarArray.equal(bytes, [var 0, 255, 0], Nat8.equal); - /// ``` - public let toVarArray : (self : Blob) -> [var Nat8] = Prim.blobToArrayMut; - - /// Returns the (non-cryptographic) hash of `blob`. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\00\FF\00" : Blob; - /// let h = Blob.hash(blob); - /// assert h == 1_818_567_776; - /// ``` - public let hash : (self : Blob) -> Types.Hash = Prim.hashBlob; - - /// General purpose comparison function for `Blob` by comparing the value of - /// the bytes. Returns the `Order` (either `#less`, `#equal`, or `#greater`) - /// by comparing `blob1` with `blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\00\00" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// let result = Blob.compare(blob1, blob2); - /// assert result == #less; - /// ``` - public func compare(self : Blob, other : Blob) : Order.Order { - let c = Prim.blobCompare(self, other); - if (c < 0) #less else if (c == 0) #equal else #greater - }; - - /// Equality function for `Blob` types. - /// This is equivalent to `blob1 == blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\FF\00" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// assert Blob.equal(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function value - /// to pass to a higher order function. - /// - /// Example: - /// ```motoko include=import - /// import List "mo:core/List"; - /// - /// let list1 = List.singleton("\00\FF\00"); - /// let list2 = List.singleton("\00\FF\00"); - /// assert List.equal(list1, list2, Blob.equal); - /// ``` - public func equal(self : Blob, other : Blob) : Bool { self == other }; - - /// Inequality function for `Blob` types. - /// This is equivalent to `blob1 != blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\AA\AA" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// assert Blob.notEqual(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func notEqual(self : Blob, other : Blob) : Bool { self != other }; - - /// "Less than" function for `Blob` types. - /// This is equivalent to `blob1 < blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\AA\AA" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// assert Blob.less(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func less(self : Blob, other : Blob) : Bool { self < other }; - - /// "Less than or equal to" function for `Blob` types. - /// This is equivalent to `blob1 <= blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\AA\AA" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// assert Blob.lessOrEqual(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func lessOrEqual(self : Blob, other : Blob) : Bool { self <= other }; - - /// "Greater than" function for `Blob` types. - /// This is equivalent to `blob1 > blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\BB\AA\AA" : Blob; - /// let blob2 = "\00\00\00" : Blob; - /// assert Blob.greater(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func greater(self : Blob, other : Blob) : Bool { self > other }; - - /// "Greater than or equal to" function for `Blob` types. - /// This is equivalent to `blob1 >= blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\BB\AA\AA" : Blob; - /// let blob2 = "\00\00\00" : Blob; - /// assert Blob.greaterOrEqual(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func greaterOrEqual(self : Blob, other : Blob) : Bool { - self >= other - }; - -} diff --git a/.mops/core@2.3.1/src/Bool.mo b/.mops/core@2.3.1/src/Bool.mo deleted file mode 100644 index b62e053..0000000 --- a/.mops/core@2.3.1/src/Bool.mo +++ /dev/null @@ -1,126 +0,0 @@ -/// Boolean type and operations. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Bool "mo:core/Bool"; -/// ``` -/// -/// While boolean operators `_ and _` and `_ or _` are short-circuiting, -/// avoiding computation of the right argument when possible, the functions -/// `logicalAnd(_, _)` and `logicalOr(_, _)` are *strict* and will always evaluate *both* -/// of their arguments. -/// -/// Example: -/// ```motoko include=import -/// let t = true; -/// let f = false; -/// -/// // Short-circuiting AND -/// assert not (t and f); -/// -/// // Short-circuiting OR -/// assert t or f; -/// ``` - -import Prim "mo:⛔"; -import Iter "Iter"; -import Order "Order"; - -module { - - /// Booleans with constants `true` and `false`. - public type Bool = Prim.Types.Bool; - - /// Returns `a and b`. - /// - /// Example: - /// ```motoko include=import - /// assert not Bool.logicalAnd(true, false); - /// assert Bool.logicalAnd(true, true); - /// ``` - public func logicalAnd(self : Bool, other : Bool) : Bool = self and other; - - /// Returns `a or b`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.logicalOr(true, false); - /// assert Bool.logicalOr(false, true); - /// ``` - public func logicalOr(self : Bool, other : Bool) : Bool = self or other; - - /// Returns exclusive or of `a` and `b`, `a != b`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.logicalXor(true, false); - /// assert not Bool.logicalXor(true, true); - /// assert not Bool.logicalXor(false, false); - /// ``` - public func logicalXor(self : Bool, other : Bool) : Bool = self != other; - - /// Returns `not bool`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.logicalNot(false); - /// assert not Bool.logicalNot(true); - /// ``` - public func logicalNot(self : Bool) : Bool = not self; - - /// Returns `a == b`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.equal(true, true); - /// assert not Bool.equal(true, false); - /// ``` - public func equal(self : Bool, other : Bool) : Bool { self == other }; - - /// Returns the ordering of `a` compared to `b`. - /// Returns `#less` if `a` is `false` and `b` is `true`, - /// `#equal` if `a` equals `b`, - /// and `#greater` if `a` is `true` and `b` is `false`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.compare(true, false) == #greater; - /// assert Bool.compare(true, true) == #equal; - /// assert Bool.compare(false, true) == #less; - /// ``` - public func compare(self : Bool, other : Bool) : Order.Order { - if (self == other) #equal else if self #greater else #less - }; - - /// Returns a text value which is either `"true"` or `"false"` depending on the input value. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.toText(true) == "true"; - /// assert Bool.toText(false) == "false"; - /// ``` - public func toText(self : Bool) : Text { - if self "true" else "false" - }; - - /// Returns an iterator over all possible boolean values (`true` and `false`). - /// - /// Example: - /// ```motoko include=import - /// let iter = Bool.allValues(); - /// assert iter.next() == ?true; - /// assert iter.next() == ?false; - /// assert iter.next() == null; - /// ``` - public func allValues() : Iter.Iter = object { - var state : ?Bool = ?true; - public func next() : ?Bool { - switch state { - case (?true) { state := ?false; ?true }; - case (?false) { state := null; ?false }; - case null { null } - } - } - }; - -} diff --git a/.mops/core@2.3.1/src/CertifiedData.mo b/.mops/core@2.3.1/src/CertifiedData.mo deleted file mode 100644 index f3ffb82..0000000 --- a/.mops/core@2.3.1/src/CertifiedData.mo +++ /dev/null @@ -1,54 +0,0 @@ -/// Certified data. -/// -/// The Internet Computer allows canister smart contracts to store a small amount of data during -/// update method processing so that during query call processing, the canister can obtain -/// a certificate about that data. -/// -/// This module provides a _low-level_ interface to this API, aimed at advanced -/// users and library implementors. See the Internet Computer Functional -/// Specification and corresponding documentation for how to use this to make query -/// calls to your canister tamperproof. - -import Prim "mo:⛔"; - -module { - - /// Set the certified data. - /// - /// Must be called from an update method, else traps. - /// Must be passed a blob of at most 32 bytes, else traps. - /// - /// Example: - /// ```motoko no-repl - /// import CertifiedData "mo:core/CertifiedData"; - /// import Blob "mo:core/Blob"; - /// - /// // Must be in an update call - /// - /// let array : [Nat8] = [1, 2, 3]; - /// let blob = Blob.fromArray(array); - /// CertifiedData.set(blob); - /// ``` - /// - /// See a full example on how to use certified variables here: https://github.com/dfinity/examples/tree/master/motoko/cert-var - /// - public let set : (data : Blob) -> () = Prim.setCertifiedData; - - /// Gets a certificate - /// - /// Returns `null` if no certificate is available, e.g. when processing an - /// update call or inter-canister call. This returns a non-`null` value only - /// when processing a query call. - /// - /// Example: - /// ```motoko no-repl - /// import CertifiedData "mo:core/CertifiedData"; - /// // Must be in a query call - /// - /// CertifiedData.getCertificate(); - /// ``` - /// See a full example on how to use certified variables here: https://github.com/dfinity/examples/tree/master/motoko/cert-var - /// - public let getCertificate : () -> ?Blob = Prim.getCertificate; - -} diff --git a/.mops/core@2.3.1/src/Char.mo b/.mops/core@2.3.1/src/Char.mo deleted file mode 100644 index 4dc75f5..0000000 --- a/.mops/core@2.3.1/src/Char.mo +++ /dev/null @@ -1,216 +0,0 @@ -/// Module for working with Characters (Unicode code points). -/// -/// Characters in Motoko represent Unicode code points -/// in the range 0 to 0x10FFFF, excluding the surrogate code points -/// (0xD800 through 0xDFFF). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Char "mo:core/Char"; -/// ``` -/// -/// Some built in features not listed in this module: -/// -/// * You can create a `Char` literal using single quotes, e.g. 'A', '1', '漢' -/// * You can compare characters using `<`, `<=`, `==`, `!=`, `>=`, `>` operators -/// * You can convert a single-character `Text` to a `Char` using `:Char` type annotation -/// -/// For example: -/// ```motoko include=import -/// let char : Char = 'A'; -/// let unicodeChar = '漢'; -/// let digit = '7'; -/// assert Char.isDigit(digit); -/// assert Char.toText(char) == "A"; -/// ``` - -import Prim "mo:⛔"; - -module { - - /// Characters represented as Unicode code points. - public type Char = Prim.Types.Char; - - /// Convert character `char` to a word containing its Unicode scalar value. - /// - /// Example: - /// ```motoko include=import - /// let char = 'A'; - /// let unicode = Char.toNat32(char); - /// assert unicode == 65; - /// ``` - public let toNat32 : (self : Char) -> Nat32 = Prim.charToNat32; - - /// Convert `w` to a character. - /// Traps if `w` is not a valid Unicode scalar value. - /// Value `w` is valid if, and only if, `w < 0xD800 or (0xE000 <= w and w <= 0x10FFFF)`. - /// - /// Example: - /// ```motoko include=import - /// let unicode : Nat32 = 65; - /// let char = Char.fromNat32(unicode); - /// assert char == 'A'; - /// ``` - public let fromNat32 : (nat32 : Nat32) -> Char = Prim.nat32ToChar; - - /// Convert character `char` to single character text. - /// - /// Example: - /// ```motoko include=import - /// let char = '漢'; - /// let text = Char.toText(char); - /// assert text == "漢"; - /// ``` - public let toText : (self : Char) -> Text = Prim.charToText; - - // Not exposed pending multi-char implementation. - private let _toUpper : (char : Char) -> Char = Prim.charToUpper; - - // Not exposed pending multi-char implementation. - private let _toLower : (char : Char) -> Char = Prim.charToLower; - - /// Returns `true` when `char` is a decimal digit between `0` and `9`, otherwise `false`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isDigit('5'); - /// assert not Char.isDigit('A'); - /// ``` - public func isDigit(self : Char) : Bool { - Prim.charToNat32(self) -% Prim.charToNat32('0') <= (9 : Nat32) - }; - - /// Returns whether `char` is a whitespace character. - /// Whitespace characters include space, tab, newline, etc. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isWhitespace(' '); - /// assert Char.isWhitespace('\n'); - /// assert not Char.isWhitespace('A'); - /// ``` - public let isWhitespace : (self : Char) -> Bool = Prim.charIsWhitespace; - - /// Returns whether `char` is a lowercase character. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isLower('a'); - /// assert not Char.isLower('A'); - /// ``` - public let isLower : (self : Char) -> Bool = Prim.charIsLowercase; - - /// Returns whether `char` is an uppercase character. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isUpper('A'); - /// assert not Char.isUpper('a'); - /// ``` - public let isUpper : (self : Char) -> Bool = Prim.charIsUppercase; - - /// Returns whether `char` is an alphabetic character. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isAlphabetic('A'); - /// assert Char.isAlphabetic('漢'); - /// assert not Char.isAlphabetic('1'); - /// ``` - public func isAlphabetic(self : Char) : Bool = Prim.charIsAlphabetic(self); - - /// Returns `a == b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.equal('A', 'A'); - /// assert not Char.equal('A', 'B'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func equal(self : Char, other : Char) : Bool { self == other }; - - /// Returns `a != b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.notEqual('A', 'B'); - /// assert not Char.notEqual('A', 'A'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func notEqual(self : Char, other : Char) : Bool { self != other }; - - /// Returns `a < b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.less('A', 'B'); - /// assert not Char.less('B', 'A'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func less(self : Char, other : Char) : Bool { self < other }; - - /// Returns `a <= b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.lessOrEqual('A', 'A'); - /// assert Char.lessOrEqual('A', 'B'); - /// assert not Char.lessOrEqual('B', 'A'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func lessOrEqual(self : Char, other : Char) : Bool { self <= other }; - - /// Returns `a > b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.greater('B', 'A'); - /// assert not Char.greater('A', 'B'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func greater(self : Char, other : Char) : Bool { self > other }; - - /// Returns `a >= b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.greaterOrEqual('B', 'A'); - /// assert Char.greaterOrEqual('A', 'A'); - /// assert not Char.greaterOrEqual('A', 'B'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func greaterOrEqual(self : Char, other : Char) : Bool { self >= other }; - - /// Returns the order of `a` and `b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.compare('A', 'B') == #less; - /// assert Char.compare('B', 'A') == #greater; - /// assert Char.compare('A', 'A') == #equal; - /// ``` - public func compare(self : Char, other : Char) : { #less; #equal; #greater } { - if (self < other) { #less } else if (self == other) { #equal } else { - #greater - } - }; - -} diff --git a/.mops/core@2.3.1/src/Cycles.mo b/.mops/core@2.3.1/src/Cycles.mo deleted file mode 100644 index 5c62828..0000000 --- a/.mops/core@2.3.1/src/Cycles.mo +++ /dev/null @@ -1,139 +0,0 @@ -/// Managing cycles within actors in the Internet Computer Protocol (ICP). -/// -/// The usage of the Internet Computer is measured, and paid for, in _cycles_. -/// This library provides imperative operations for observing cycles, transferring cycles, and -/// observing refunds of cycles. -/// -/// **NOTE:** Since cycles measure computational resources, the value of `balance()` can change from one call to the next. -/// -/// Cycles can be transferred from the current actor to another actor with the evaluation of certain forms of expression. -/// In particular, the expression must be a call to a shared function, a call to a local function with an `async` return type, or a simple `async` expression. -/// To attach an amount of cycles to an expression ``, simply prefix the expression with `(with cycles = )`, that is, `(with cycles = ) `. -/// -/// **NOTE:** Attaching cycles will trap if the amount specified exceeds `2 ** 128` cycles. -/// -/// Upon the call, but not before, the amount of cycles is deducted from `balance()`. -/// If this total exceeds `balance()`, the caller traps, aborting the call without consuming the cycles. -/// Note that attaching cycles to a call to a local function call or `async` expression just transfers cycles from the current actor to itself. -/// -/// Example for use on the ICP: -/// ```motoko no-repl -/// import Cycles "mo:core/Cycles"; -/// -/// persistent actor { -/// public func main() : async () { -/// let initialBalance = Cycles.balance(); -/// await (with cycles = 15_000_000) operation(); // accepts 10_000_000 cycles -/// assert Cycles.refunded() == 5_000_000; -/// assert Cycles.balance() < initialBalance; // decreased by around 10_000_000 -/// }; -/// -/// func operation() : async () { -/// let initialBalance = Cycles.balance(); -/// let initialAvailable = Cycles.available(); -/// let obtained = Cycles.accept(10_000_000); -/// assert obtained == 10_000_000; -/// assert Cycles.balance() == initialBalance + 10_000_000; -/// assert Cycles.available() == initialAvailable - 10_000_000; -/// } -/// } -/// ``` -import Prim "mo:⛔"; -module { - - /// Returns the actor's current balance of cycles as `amount`. - /// - /// Example for use on the ICP: - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// public func main() : async() { - /// let balance = Cycles.balance(); - /// assert balance > 0; - /// } - /// } - /// ``` - public let balance : () -> (amount : Nat) = Prim.cyclesBalance; - - /// Returns the currently available `amount` of cycles. - /// The amount available is the amount received in the current call, - /// minus the cumulative amount `accept`ed by this call. - /// On exit from the current shared function or async expression via `return` or `throw`, - /// any remaining available amount is automatically refunded to the caller/context. - /// - /// Example for use on the ICP: - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// public func main() : async() { - /// let available = Cycles.available(); - /// assert available >= 0; - /// } - /// } - /// ``` - public let available : () -> (amount : Nat) = Prim.cyclesAvailable; - - /// Transfers up to `amount` from `available()` to `balance()`. - /// Returns the amount actually transferred, which may be less than - /// requested, for example, if less is available, or if canister balance limits are reached. - /// - /// Example for use on the ICP (for simplicity, only transferring cycles to itself): - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// public func main() : async() { - /// await (with cycles = 15_000_000) operation(); // accepts 10_000_000 cycles - /// }; - /// - /// func operation() : async() { - /// let obtained = Cycles.accept(10_000_000); - /// assert obtained == 10_000_000; - /// } - /// } - /// ``` - public let accept : (amount : Nat) -> (accepted : Nat) = Prim.cyclesAccept; - - /// Reports `amount` of cycles refunded in the last `await` of the current - /// context, or zero if no await has occurred yet. - /// Calling `refunded()` is solely informational and does not affect `balance()`. - /// Instead, refunds are automatically added to the current balance, - /// whether or not `refunded` is used to observe them. - /// - /// Example for use on the ICP (for simplicity, only transferring cycles to itself): - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// func operation() : async() { - /// ignore Cycles.accept(10_000_000); - /// }; - /// - /// public func main() : async() { - /// await (with cycles = 15_000_000) operation(); // accepts 10_000_000 cycles - /// assert Cycles.refunded() == 5_000_000; - /// } - /// } - /// ``` - public let refunded : () -> (amount : Nat) = Prim.cyclesRefunded; - - /// Attempts to burn `amount` of cycles, deducting `burned` from the canister's - /// cycle balance. The burned cycles are irrevocably lost and not available to any - /// other principal either. - /// - /// Example for use on the IC: - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// public func main() : async() { - /// let burnt = Cycles.burn(10_000_000); - /// assert burnt == 10_000_000; - /// } - /// } - /// ``` - public let burn : (amount : Nat) -> (burned : Nat) = Prim.cyclesBurn; - -} diff --git a/.mops/core@2.3.1/src/Debug.mo b/.mops/core@2.3.1/src/Debug.mo deleted file mode 100644 index 7727a8a..0000000 --- a/.mops/core@2.3.1/src/Debug.mo +++ /dev/null @@ -1,39 +0,0 @@ -/// Utility functions for debugging. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Debug "mo:core/Debug"; -/// ``` - -import Prim "mo:⛔"; -import Runtime "Runtime"; - -module { - - /// Prints `text` to output stream. - /// - /// NOTE: When running on an ICP network, all output is written to the [canister log](https://internetcomputer.org/docs/building-apps/canister-management/logs) with the exclusion of any output - /// produced during the execution of non-replicated queries and composite queries. - /// In other environments, like the interpreter and stand-alone wasm engines, the output is written to standard out. - /// - /// ```motoko include=import - /// Debug.print "Hello New World!"; - /// Debug.print(debug_show(4)) // Often used with `debug_show` to convert values to Text - /// ``` - public let print : (text : Text) -> () = Prim.debugPrint; - - /// Mark incomplete code with the `todo()` function. - /// - /// Each have calls are well-typed in all typing contexts, which - /// trap in all execution contexts. - /// - /// ```motoko include=import - /// func doSomethingComplex() { - /// Debug.todo() - /// }; - /// ``` - public func todo() : None { - Runtime.trap("Debug.todo()") - }; - -} diff --git a/.mops/core@2.3.1/src/Error.mo b/.mops/core@2.3.1/src/Error.mo deleted file mode 100644 index cf73496..0000000 --- a/.mops/core@2.3.1/src/Error.mo +++ /dev/null @@ -1,106 +0,0 @@ -/// Error values and inspection. -/// -/// The `Error` type is the argument to `throw`, parameter of `catch`. -/// The `Error` type is opaque. - -import Prim "mo:⛔"; - -module { - - /// Error value resulting from `async` computations - public type Error = Prim.Types.Error; - - /// Error code to classify different kinds of user and system errors: - /// ```motoko - /// type ErrorCode = { - /// // Fatal error. - /// #system_fatal; - /// // Transient error. - /// #system_transient; - /// // Destination invalid. - /// #destination_invalid; - /// // Canister error (e.g., trap, no response). - /// #canister_error; - /// // Explicit reject by canister code. - /// #canister_reject; - /// // Response unknown; system stopped waiting for it (e.g., timed out, or system under high load). - /// #system_unknown; - /// // Future error code (with unrecognized numeric code). - /// #future : Nat32; - /// // Error issuing inter-canister call - /// // (indicating destination queue full or freezing threshold crossed). - /// #call_error : { err_code : Nat32 } - /// }; - /// ``` - public type ErrorCode = Prim.ErrorCode; - - /// Create an error from the message with the code `#canister_reject`. - /// - /// Example: - /// ```motoko - /// import Error "mo:core/Error"; - /// - /// Error.reject("Example error") // can be used as throw argument - /// ``` - public let reject : (message : Text) -> Error = Prim.error; - - /// Returns the code of an error. - /// - /// Example: - /// ```motoko - /// import Error "mo:core/Error"; - /// - /// let error = Error.reject("Example error"); - /// Error.code(error) // #canister_reject - /// ``` - public let code : (self : Error) -> ErrorCode = Prim.errorCode; - - /// Returns the message of an error. - /// - /// Example: - /// ```motoko - /// import Error "mo:core/Error"; - /// - /// let error = Error.reject("Example error"); - /// Error.message(error) // "Example error" - /// ``` - public let message : (self : Error) -> Text = Prim.errorMessage; - - /// Checks if the error is a clean reject. - /// A clean reject means that there must be no state changes on the callee side. - public func isCleanReject(self : Error) : Bool = switch (code(self)) { - case (#system_fatal or #system_transient or #destination_invalid or #call_error _) true; - case _ false - }; - - /// Returns whether retrying to send a message may result in success. - /// - /// Example: - /// ```motoko - /// import Error "mo:core/Error"; - /// import Debug "mo:core/Debug"; - /// - /// persistent actor { - /// type CallableActor = actor { - /// call : () -> async () - /// }; - /// - /// public func example(callableActor : CallableActor) { - /// try { - /// await (with timeout = 3) callableActor.call(); - /// } - /// catch e { - /// if (Error.isRetryPossible e) { - /// Debug.print(Error.message e); - /// } - /// } - /// } - /// } - /// - /// ``` - public func isRetryPossible(self : Error) : Bool = switch (code(self)) { - case (#system_transient or #system_unknown) true; - case _ false - }; - -} diff --git a/.mops/core@2.3.1/src/Float.mo b/.mops/core@2.3.1/src/Float.mo deleted file mode 100644 index 49f1852..0000000 --- a/.mops/core@2.3.1/src/Float.mo +++ /dev/null @@ -1,809 +0,0 @@ -/// Double precision (64-bit) floating-point numbers in IEEE 754 representation. -/// -/// This module contains common floating-point constants and utility functions. -/// -/// ```motoko name=import -/// import Float "mo:core/Float"; -/// ``` -/// -/// Notation for special values in the documentation below: -/// `+inf`: Positive infinity -/// `-inf`: Negative infinity -/// `NaN`: "not a number" (can have different sign bit values, but `NaN != NaN` regardless of the sign). -/// -/// Note: -/// Floating point numbers have limited precision and operations may inherently result in numerical errors. -/// -/// Examples of numerical errors: -/// ```motoko -/// assert 0.1 + 0.1 + 0.1 != 0.3; -/// ``` -/// -/// ```motoko -/// assert not (1e16 + 1.0 != 1e16); -/// ``` -/// -/// (and many more cases) -/// -/// Advice: -/// * Floating point number comparisons by `==` or `!=` are discouraged. Instead, it is better to compare -/// floating-point numbers with a numerical tolerance, called epsilon. -/// -/// Example: -/// ```motoko -/// import Float "mo:core/Float"; -/// let x = 0.1 + 0.1 + 0.1; -/// let y = 0.3; -/// -/// let epsilon = 1e-6; // This depends on the application case (needs a numerical error analysis). -/// assert Float.equal(x, y, epsilon); -/// ``` -/// -/// * For absolute precision, it is recommened to encode the fraction number as a pair of a Nat for the base -/// and a Nat for the exponent (decimal point). -/// -/// NaN sign: -/// * The NaN sign is only applied by `abs`, `neg`, and `copySign`. Other operations can have an arbitrary -/// sign bit for NaN results. - -import Prim "mo:⛔"; -import Int "Int"; -import Order "Order"; - -module { - - /// 64-bit floating point number type. - public type Float = Prim.Types.Float; - - /// Ratio of the circumference of a circle to its diameter. - /// Note: Limited precision. - public let pi : Float = 3.14159265358979323846; // taken from musl math.h - - /// Base of the natural logarithm. - /// Note: Limited precision. - public let e : Float = 2.7182818284590452354; // taken from musl math.h - - /// Determines whether the `number` is a `NaN` ("not a number" in the floating point representation). - /// Notes: - /// * Equality test of `NaN` with itself or another number is always `false`. - /// * There exist many internal `NaN` value representations, such as positive and negative NaN, - /// signalling and quiet NaNs, each with many different bit representations. - /// - /// Example: - /// ```motoko include=import - /// assert Float.isNaN(0.0/0.0); - /// ``` - public func isNaN(self : Float) : Bool { - self != self - }; - - /// Returns the absolute value of `x`. - /// - /// Special cases: - /// ``` - /// abs(+inf) => +inf - /// abs(-inf) => +inf - /// abs(-NaN) => +NaN - /// abs(-0.0) => 0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.abs(-1.2), 1.2, epsilon); - /// ``` - public let abs : (x : Float) -> Float = Prim.floatAbs; - - /// Returns the square root of `x`. - /// - /// Special cases: - /// ``` - /// sqrt(+inf) => +inf - /// sqrt(-0.0) => -0.0 - /// sqrt(x) => NaN if x < 0.0 - /// sqrt(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.sqrt(6.25), 2.5, epsilon); - /// ``` - public let sqrt : (x : Float) -> Float = Prim.floatSqrt; - - /// Returns the smallest integral float greater than or equal to `x`. - /// - /// Special cases: - /// ``` - /// ceil(+inf) => +inf - /// ceil(-inf) => -inf - /// ceil(NaN) => NaN - /// ceil(0.0) => 0.0 - /// ceil(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.ceil(1.2), 2.0, epsilon); - /// ``` - public let ceil : (x : Float) -> Float = Prim.floatCeil; - - /// Returns the largest integral float less than or equal to `x`. - /// - /// Special cases: - /// ``` - /// floor(+inf) => +inf - /// floor(-inf) => -inf - /// floor(NaN) => NaN - /// floor(0.0) => 0.0 - /// floor(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.floor(1.2), 1.0, epsilon); - /// ``` - public let floor : (x : Float) -> Float = Prim.floatFloor; - - /// Returns the nearest integral float not greater in magnitude than `x`. - /// This is equivalent to returning `x` with truncating its decimal places. - /// - /// Special cases: - /// ``` - /// trunc(+inf) => +inf - /// trunc(-inf) => -inf - /// trunc(NaN) => NaN - /// trunc(0.0) => 0.0 - /// trunc(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.trunc(2.75), 2.0, epsilon); - /// ``` - public let trunc : (x : Float) -> Float = Prim.floatTrunc; - - /// Returns the nearest integral float to `x`. - /// A decimal place of exactly .5 is rounded to the nearest even integral float. - /// and rounded down for `x < 0` - /// - /// Special cases: - /// ``` - /// nearest(+inf) => +inf - /// nearest(-inf) => -inf - /// nearest(NaN) => NaN - /// nearest(0.0) => 0.0 - /// nearest(-0.0) => -0.0 - /// nearest(14.5) => 14.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.nearest(2.75) == 3.0 - /// ``` - public let nearest : (x : Float) -> Float = Prim.floatNearest; - - /// Returns `x` if `x` and `y` have same sign, otherwise `x` with negated sign. - /// - /// The sign bit of zero, infinity, and `NaN` is considered. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.copySign(1.2, -2.3), -1.2, epsilon); - /// ``` - public let copySign : (x : Float, y : Float) -> Float = Prim.floatCopySign; - - /// Returns the smaller value of `x` and `y`. - /// - /// Special cases: - /// ``` - /// min(NaN, y) => NaN for any Float y - /// min(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.min(1.2, -2.3) == -2.3; // with numerical imprecision - /// ``` - public let min : (x : Float, y : Float) -> Float = Prim.floatMin; - - /// Returns the larger value of `x` and `y`. - /// - /// Special cases: - /// ``` - /// max(NaN, y) => NaN for any Float y - /// max(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.max(1.2, -2.3) == 1.2; - /// ``` - public let max : (x : Float, y : Float) -> Float = Prim.floatMax; - - /// Returns the sine of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// sin(+inf) => NaN - /// sin(-inf) => NaN - /// sin(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.sin(Float.pi / 2), 1.0, epsilon); - /// ``` - public let sin : (x : Float) -> Float = Prim.sin; - - /// Returns the cosine of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// cos(+inf) => NaN - /// cos(-inf) => NaN - /// cos(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.cos(Float.pi / 2), 0.0, epsilon); - /// ``` - public let cos : (x : Float) -> Float = Prim.cos; - - /// Returns the tangent of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// tan(+inf) => NaN - /// tan(-inf) => NaN - /// tan(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.tan(Float.pi / 4), 1.0, epsilon); - /// ``` - public let tan : (x : Float) -> Float = Prim.tan; - - /// Returns the arc sine of `x` in radians. - /// - /// Special cases: - /// ``` - /// arcsin(x) => NaN if x > 1.0 - /// arcsin(x) => NaN if x < -1.0 - /// arcsin(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.arcsin(1.0), Float.pi / 2, epsilon); - /// ``` - public let arcsin : (x : Float) -> Float = Prim.arcsin; - - /// Returns the arc cosine of `x` in radians. - /// - /// Special cases: - /// ``` - /// arccos(x) => NaN if x > 1.0 - /// arccos(x) => NaN if x < -1.0 - /// arcos(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.arccos(1.0), 0.0, epsilon); - /// ``` - public let arccos : (x : Float) -> Float = Prim.arccos; - - /// Returns the arc tangent of `x` in radians. - /// - /// Special cases: - /// ``` - /// arctan(+inf) => pi / 2 - /// arctan(-inf) => -pi / 2 - /// arctan(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.arctan(1.0), Float.pi / 4, epsilon); - /// ``` - public let arctan : (x : Float) -> Float = Prim.arctan; - - /// Given `(y,x)`, returns the arc tangent in radians of `y/x` based on the signs of both values to determine the correct quadrant. - /// - /// Special cases: - /// ``` - /// arctan2(0.0, 0.0) => 0.0 - /// arctan2(-0.0, 0.0) => -0.0 - /// arctan2(0.0, -0.0) => pi - /// arctan2(-0.0, -0.0) => -pi - /// arctan2(+inf, +inf) => pi / 4 - /// arctan2(+inf, -inf) => 3 * pi / 4 - /// arctan2(-inf, +inf) => -pi / 4 - /// arctan2(-inf, -inf) => -3 * pi / 4 - /// arctan2(NaN, x) => NaN for any Float x - /// arctan2(y, NaN) => NaN for any Float y - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let sqrt2over2 = Float.sqrt(2) / 2; - /// assert Float.arctan2(sqrt2over2, sqrt2over2) == Float.pi / 4; - /// ``` - public let arctan2 : (x : Float, y : Float) -> Float = Prim.arctan2; - - /// Returns the value of `e` raised to the `x`-th power. - /// - /// Special cases: - /// ``` - /// exp(+inf) => +inf - /// exp(-inf) => 0.0 - /// exp(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.exp(1.0), Float.e, epsilon); - /// ``` - public let exp : (x : Float) -> Float = Prim.exp; - - /// Returns the natural logarithm (base-`e`) of `x`. - /// - /// Special cases: - /// ``` - /// log(0.0) => -inf - /// log(-0.0) => -inf - /// log(x) => NaN if x < 0.0 - /// log(+inf) => +inf - /// log(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.log(Float.e), 1.0, epsilon); - /// ``` - public let log : (x : Float) -> Float = Prim.log; - - /// Formatting. `format(fmt, x)` formats `x` to `Text` according to the - /// formatting directive `fmt`, which can take one of the following forms: - /// - /// * `#fix prec` as fixed-point format with `prec` digits - /// * `#exp prec` as exponential format with `prec` digits - /// * `#gen prec` as generic format with `prec` digits - /// * `#exact` as exact format that can be decoded without loss. - /// - /// `-0.0` is formatted with negative sign bit. - /// Positive infinity is formatted as "inf". - /// Negative infinity is formatted as "-inf". - /// - /// The numerical precision and the text format can vary between - /// Motoko versions and runtime configuration. Moreover, `NaN` can be printed - /// differently, i.e. "NaN" or "nan", potentially omitting the `NaN` sign. - /// - /// Example: - /// ```motoko include=import no-validate - /// assert Float.format(#exp 3, 123.0) == "1.230e+02"; - /// ``` - public func format(self : Float, fmt : { #fix : Nat8; #exp : Nat8; #gen : Nat8; #exact }) : Text = switch fmt { - case (#fix(prec)) { Prim.floatToFormattedText(self, prec, 0) }; - case (#exp(prec)) { Prim.floatToFormattedText(self, prec, 1) }; - case (#gen(prec)) { Prim.floatToFormattedText(self, prec, 2) }; - case (#exact) { Prim.floatToFormattedText(self, 17, 2) } - }; - - /// Conversion to Text. Use `format(fmt, x)` for more detailed control. - /// - /// `-0.0` is formatted with negative sign bit. - /// Positive infinity is formatted as `inf`. - /// Negative infinity is formatted as `-inf`. - /// `NaN` is formatted as `NaN` or `-NaN` depending on its sign bit. - /// - /// The numerical precision and the text format can vary between - /// Motoko versions and runtime configuration. Moreover, `NaN` can be printed - /// differently, i.e. "NaN" or "nan", potentially omitting the `NaN` sign. - /// - /// Example: - /// ```motoko include=import no-validate - /// assert Float.toText(1.2) == "1.2"; - /// ``` - public let toText : (self : Float) -> Text = Prim.floatToText; - - /// Conversion to Int64 by truncating Float, equivalent to `toInt64(trunc(f))` - /// - /// Traps if the floating point number is larger or smaller than the representable Int64. - /// Also traps for `inf`, `-inf`, and `NaN`. - /// - /// Example: - /// ```motoko include=import - /// assert Float.toInt64(-12.3) == -12; - /// ``` - public let toInt64 : (self : Float) -> Int64 = Prim.floatToInt64; - - /// Conversion from Int64. - /// - /// Note: The floating point number may be imprecise for large or small Int64. - /// - /// Example: - /// ```motoko include=import - /// assert Float.fromInt64(-42) == -42.0; - /// ``` - public let fromInt64 : (x : Int64) -> Float = Prim.int64ToFloat; - - /// Conversion to Int. - /// - /// Traps for `inf`, `-inf`, and `NaN`. - /// - /// Example: - /// ```motoko include=import - /// assert Float.toInt(1.2e6) == +1_200_000; - /// ``` - public let toInt : (self : Float) -> Int = Prim.floatToInt; - - /// Conversion from Int. May result in `Inf`. - /// - /// Note: The floating point number may be imprecise for large or small Int values. - /// Returns `inf` if the integer is greater than the maximum floating point number. - /// Returns `-inf` if the integer is less than the minimum floating point number. - /// - /// Example: - /// ```motoko include=import - /// assert Float.fromInt(-123) == -123.0; - /// ``` - /// @deprecated M0235 - public let fromInt : (x : Int) -> Float = Prim.intToFloat; - - /// Determines whether `x` is equal to `y` within the defined tolerance of `epsilon`. - /// The `epsilon` considers numerical erros, see comment above. - /// Equivalent to `Float.abs(x - y) <= epsilon` for a non-negative epsilon. - /// - /// Traps if `epsilon` is negative or `NaN`. - /// - /// Special cases: - /// ``` - /// equal(+0.0, -0.0, epsilon) => true for any `epsilon >= 0.0` - /// equal(-0.0, +0.0, epsilon) => true for any `epsilon >= 0.0` - /// equal(+inf, +inf, epsilon) => true for any `epsilon >= 0.0` - /// equal(-inf, -inf, epsilon) => true for any `epsilon >= 0.0` - /// equal(x, NaN, epsilon) => false for any x and `epsilon >= 0.0` - /// equal(NaN, y, epsilon) => false for any y and `epsilon >= 0.0` - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(-12.3, -1.23e1, epsilon); - /// ``` - public func equal(x : Float, y : Float, epsilon : Float) : Bool { - if (not (epsilon >= 0.0)) { - // also considers NaN, not identical to `epsilon < 0.0` - Prim.trap("Float.equal(): epsilon must be greater or equal 0.0") - }; - x == y or abs(x - y) <= epsilon // `x == y` to also consider infinity equal - }; - - /// Determines whether `x` is not equal to `y` within the defined tolerance of `epsilon`. - /// The `epsilon` considers numerical erros, see comment above. - /// Equivalent to `not equal(x, y, epsilon)`. - /// - /// Traps if `epsilon` is negative or `NaN`. - /// - /// Special cases: - /// ``` - /// notEqual(+0.0, -0.0, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(-0.0, +0.0, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(+inf, +inf, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(-inf, -inf, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(x, NaN, epsilon) => true for any x and `epsilon >= 0.0` - /// notEqual(NaN, y, epsilon) => true for any y and `epsilon >= 0.0` - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert not Float.notEqual(-12.3, -1.23e1, epsilon); - /// ``` - public func notEqual(x : Float, y : Float, epsilon : Float) : Bool { - if (not (epsilon >= 0.0)) { - // also considers NaN, not identical to `epsilon < 0.0` - Prim.trap("Float.notEqual(): epsilon must be greater or equal 0.0") - }; - not (x == y or abs(x - y) <= epsilon) - }; - - /// Returns `x < y`. - /// - /// Special cases: - /// ``` - /// less(+0.0, -0.0) => false - /// less(-0.0, +0.0) => false - /// less(NaN, y) => false for any Float y - /// less(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.less(Float.e, Float.pi); - /// ``` - public func less(x : Float, y : Float) : Bool { x < y }; - - /// Returns `x <= y`. - /// - /// Special cases: - /// ``` - /// lessOrEqual(+0.0, -0.0) => true - /// lessOrEqual(-0.0, +0.0) => true - /// lessOrEqual(NaN, y) => false for any Float y - /// lessOrEqual(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.lessOrEqual(0.123, 0.1234); - /// ``` - public func lessOrEqual(x : Float, y : Float) : Bool { x <= y }; - - /// Returns `x > y`. - /// - /// Special cases: - /// ``` - /// greater(+0.0, -0.0) => false - /// greater(-0.0, +0.0) => false - /// greater(NaN, y) => false for any Float y - /// greater(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.greater(Float.pi, Float.e); - /// ``` - public func greater(x : Float, y : Float) : Bool { x > y }; - - /// Returns `x >= y`. - /// - /// Special cases: - /// ``` - /// greaterOrEqual(+0.0, -0.0) => true - /// greaterOrEqual(-0.0, +0.0) => true - /// greaterOrEqual(NaN, y) => false for any Float y - /// greaterOrEqual(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.greaterOrEqual(0.1234, 0.123); - /// ``` - public func greaterOrEqual(x : Float, y : Float) : Bool { - x >= y - }; - - /// Defines a total order of `x` and `y` for use in sorting. - /// - /// Note: Using this operation to determine equality or inequality is discouraged for two reasons: - /// * It does not consider numerical errors, see comment above. Use `equal(x, y, espilon)` or - /// `notEqual(x, y, epsilon)` to test for equality or inequality, respectively. - /// * `NaN` are here considered equal if their sign matches, which is different to the standard equality - /// by `==` or when using `equal()` or `notEqual()`. - /// - /// Total order: - /// * negative NaN (no distinction between signalling and quiet negative NaN) - /// * negative infinity - /// * negative numbers (including negative subnormal numbers in standard order) - /// * negative zero (`-0.0`) - /// * positive zero (`+0.0`) - /// * positive numbers (including positive subnormal numbers in standard order) - /// * positive infinity - /// * positive NaN (no distinction between signalling and quiet positive NaN) - /// - /// Example: - /// ```motoko include=import - /// assert Float.compare(0.123, 0.1234) == #less; - /// ``` - public func compare(x : Float, y : Float) : Order.Order { - if (isNaN(x)) { - if (isNegative(x)) { - if (isNaN(y) and isNegative(y)) { #equal } else { #less } - } else { - if (isNaN(y) and not isNegative(y)) { #equal } else { #greater } - } - } else if (isNaN(y)) { - if (isNegative(y)) { - #greater - } else { - #less - } - } else { - if (x == y) { #equal } else if (x < y) { #less } else { - #greater - } - } - }; - - func isNegative(self : Float) : Bool { - copySign(1.0, self) < 0.0 - }; - - /// Returns the negation of `x`, `-x` . - /// - /// Changes the sign bit for infinity. - /// - /// Special cases: - /// ``` - /// neg(+inf) => -inf - /// neg(-inf) => +inf - /// neg(+NaN) => -NaN - /// neg(-NaN) => +NaN - /// neg(+0.0) => -0.0 - /// neg(-0.0) => +0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.neg(1.23), -1.23, epsilon); - /// ``` - public func neg(x : Float) : Float { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// add(+inf, y) => +inf if y is any Float except -inf and NaN - /// add(-inf, y) => -inf if y is any Float except +inf and NaN - /// add(+inf, -inf) => NaN - /// add(NaN, y) => NaN for any Float y - /// ``` - /// The same cases apply commutatively, i.e. for `add(y, x)`. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.add(1.23, 0.123), 1.353, epsilon); - /// ``` - public func add(x : Float, y : Float) : Float { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// sub(+inf, y) => +inf if y is any Float except +inf or NaN - /// sub(-inf, y) => -inf if y is any Float except -inf and NaN - /// sub(x, +inf) => -inf if x is any Float except +inf and NaN - /// sub(x, -inf) => +inf if x is any Float except -inf and NaN - /// sub(+inf, +inf) => NaN - /// sub(-inf, -inf) => NaN - /// sub(NaN, y) => NaN for any Float y - /// sub(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.sub(1.23, 0.123), 1.107, epsilon); - /// ``` - public func sub(x : Float, y : Float) : Float { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// mul(+inf, y) => +inf if y > 0.0 - /// mul(-inf, y) => -inf if y > 0.0 - /// mul(+inf, y) => -inf if y < 0.0 - /// mul(-inf, y) => +inf if y < 0.0 - /// mul(+inf, 0.0) => NaN - /// mul(-inf, 0.0) => NaN - /// mul(NaN, y) => NaN for any Float y - /// ``` - /// The same cases apply commutatively, i.e. for `mul(y, x)`. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.mul(1.23, 1e2), 123.0, epsilon); - /// ``` - public func mul(x : Float, y : Float) : Float { x * y }; - - /// Returns the division of `x` by `y`, `x / y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// div(0.0, 0.0) => NaN - /// div(x, 0.0) => +inf for x > 0.0 - /// div(x, 0.0) => -inf for x < 0.0 - /// div(x, +inf) => 0.0 for any x except +inf, -inf, and NaN - /// div(x, -inf) => 0.0 for any x except +inf, -inf, and NaN - /// div(+inf, y) => +inf if y >= 0.0 - /// div(+inf, y) => -inf if y < 0.0 - /// div(-inf, y) => -inf if y >= 0.0 - /// div(-inf, y) => +inf if y < 0.0 - /// div(NaN, y) => NaN for any Float y - /// div(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.div(1.23, 1e2), 0.0123, epsilon); - /// ``` - public func div(x : Float, y : Float) : Float { x / y }; - - /// Returns the floating point division remainder `x % y`, - /// which is defined as `x - trunc(x / y) * y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// rem(0.0, 0.0) => NaN - /// rem(x, y) => +inf if sign(x) == sign(y) for any x and y not being +inf, -inf, or NaN - /// rem(x, y) => -inf if sign(x) != sign(y) for any x and y not being +inf, -inf, or NaN - /// rem(x, +inf) => x for any x except +inf, -inf, and NaN - /// rem(x, -inf) => x for any x except +inf, -inf, and NaN - /// rem(+inf, y) => NaN for any Float y - /// rem(-inf, y) => NaN for any Float y - /// rem(NaN, y) => NaN for any Float y - /// rem(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.rem(7.2, 2.3), 0.3, epsilon); - /// ``` - public func rem(x : Float, y : Float) : Float { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// pow(+inf, y) => +inf for any y > 0.0 including +inf - /// pow(+inf, 0.0) => 1.0 - /// pow(+inf, y) => 0.0 for any y < 0.0 including -inf - /// pow(x, +inf) => +inf if x > 0.0 or x < 0.0 - /// pow(0.0, +inf) => 0.0 - /// pow(x, -inf) => 0.0 if x > 0.0 or x < 0.0 - /// pow(0.0, -inf) => +inf - /// pow(x, y) => NaN if x < 0.0 and y is a non-integral Float - /// pow(-inf, y) => +inf if y > 0.0 and y is a non-integral or an even integral Float - /// pow(-inf, y) => -inf if y > 0.0 and y is an odd integral Float - /// pow(-inf, 0.0) => 1.0 - /// pow(-inf, y) => 0.0 if y < 0.0 - /// pow(-inf, +inf) => +inf - /// pow(-inf, -inf) => 1.0 - /// pow(NaN, y) => NaN if y != 0.0 - /// pow(NaN, 0.0) => 1.0 - /// pow(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.pow(2.5, 2.0), 6.25, epsilon); - /// ``` - public func pow(x : Float, y : Float) : Float { x ** y }; - -} diff --git a/.mops/core@2.3.1/src/Func.mo b/.mops/core@2.3.1/src/Func.mo deleted file mode 100644 index e2bb10c..0000000 --- a/.mops/core@2.3.1/src/Func.mo +++ /dev/null @@ -1,48 +0,0 @@ -/// Functions on functions, creating functions from simpler inputs. -/// -/// (Most commonly used when programming in functional style using higher-order -/// functions.) -/// -/// Import from the core package to use this module. -/// -/// ```motoko name=import -/// import Func = "mo:core/Func"; -/// ``` - -module { - - /// The composition of two functions `f` and `g` is a function that applies `g` and then `f`. - /// - /// Example: - /// ```motoko include=import - /// import Text "mo:core/Text"; - /// import Char "mo:core/Char"; - /// - /// let textFromNat32 = Func.compose(Text.fromChar, Char.fromNat32); - /// assert textFromNat32(65) == "A"; - /// ``` - public func compose(f : B -> C, g : A -> B) : A -> C { - func(x : A) : C { - f(g(x)) - } - }; - - /// The `identity` function returns its argument. - /// Example: - /// ```motoko include=import - /// assert Func.identity(10) == 10; - /// assert Func.identity(true) == true; - /// ``` - public func identity(x : A) : A = x; - - /// The const function is a _curried_ function that accepts an argument `x`, - /// and then returns a function that discards its argument and always returns - /// the `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Func.const(10)("hello") == 10; - /// assert Func.const(true)(20) == true; - /// ``` - public func const(x : A) : B -> A = func _ = x -} diff --git a/.mops/core@2.3.1/src/Int.mo b/.mops/core@2.3.1/src/Int.mo deleted file mode 100644 index 37ea9b7..0000000 --- a/.mops/core@2.3.1/src/Int.mo +++ /dev/null @@ -1,677 +0,0 @@ -/// Signed integer numbers with infinite precision (also called big integers). -/// -/// Most operations on integer numbers (e.g. addition) are available as built-in operators (e.g. `-1 + 1`). -/// This module provides equivalent functions and `Text` conversion. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int "mo:core/Int"; -/// ``` - -import Prim "mo:⛔"; -import Char "Char"; -import Runtime "Runtime"; -import Iter "Iter"; -import Order "Order"; - -module { - - /// Infinite precision signed integers. - public type Int = Prim.Types.Int; - - /// Returns the absolute value of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.abs(-12) == 12; - /// ``` - public let abs : (x : Int) -> Nat = Prim.abs; - - /// Converts an integer number to its textual representation. Textual - /// representation _do not_ contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toText(-1234) == "-1234"; - /// ``` - public func toText(self : Int) : Text { - if (self == 0) { - return "0" - }; - - let isNegative = self < 0; - var int = if isNegative { -self } else { self }; - - var text = ""; - let base = 10; - - while (int > 0) { - let rem = int % base; - text := ( - switch (rem) { - case 0 { "0" }; - case 1 { "1" }; - case 2 { "2" }; - case 3 { "3" }; - case 4 { "4" }; - case 5 { "5" }; - case 6 { "6" }; - case 7 { "7" }; - case 8 { "8" }; - case 9 { "9" }; - case _ { Runtime.unreachable() } - } - ) # text; - int := int / base - }; - - return if isNegative { "-" # text } else { text } - }; - - /// Creates a integer from its textual representation. Returns `null` - /// if the input is not a valid integer. - /// - /// The textual representation _must not_ contain underscores but may - /// begin with a '+' or '-' character. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromText("-1234") == ?-1234; - /// ``` - public func fromText(text : Text) : ?Int { - if (text == "") { - return null - }; - var n = 0; - var isFirst = true; - var isNegative = false; - var hasDigits = false; - for (c in text.chars()) { - if (isFirst and c == '+') { - // Skip character - } else if (isFirst and c == '-') { - isNegative := true - } else if (Char.isDigit(c)) { - hasDigits := true; - let charAsNat = Prim.nat32ToNat(Prim.charToNat32(c) -% Prim.charToNat32('0')); - n := n * 10 + charAsNat - } else { - return null - }; - isFirst := false - }; - if (not hasDigits) { - return null - }; - ?(if (isNegative) { -n } else { n }) - }; - - /// Creates a integer from its textual representation. Returns `null` - /// if the input is not a valid integer. - /// - /// This functions is meant to be used with contextual-dot notation. - /// - /// Example: - /// ```motoko include=import - /// assert "-1234".toInt() == ?-1234; - /// ``` - public func toInt(self : Text) : ?Int { - fromText(self) - }; - - /// Converts an integer to a natural number. Traps if the integer is negative. - /// - /// Example: - /// ```motoko include=import - /// import Debug "mo:core/Debug"; - /// assert Int.toNat(1234 : Int) == (1234 : Nat); - /// ``` - public func toNat(self : Int) : Nat { - if (self < 0) { - Runtime.trap("Int.toNat(): negative input value") - } else { - abs(self) - } - }; - - /// Converts a natural number to an integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromNat(1234 : Nat) == (1234 : Int); - /// ``` - public func fromNat(nat : Nat) : Int { - nat : Int - }; - - /// Conversion to Float. May result in `Inf`. - /// - /// Note: The floating point number may be imprecise for large or small Int values. - /// Returns `inf` if the integer is greater than the maximum floating point number. - /// Returns `-inf` if the integer is less than the minimum floating point number. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toFloat(-123) == -123.0; - /// ``` - public let toFloat : (self : Int) -> Float = Prim.intToFloat; - - /// Converts a signed integer with infinite precision to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toInt8(123) == (123 : Int8); - /// ``` - public let toInt8 : (self : Int) -> Int8 = Prim.intToInt8; - - /// Converts a signed integer with infinite precision to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toInt16(12_345) == (12_345 : Int16); - /// ``` - public let toInt16 : (self : Int) -> Int16 = Prim.intToInt16; - - /// Converts a signed integer with infinite precision to a 32-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toInt32(123_456) == (123_456 : Int32); - /// ``` - public let toInt32 : (self : Int) -> Int32 = Prim.intToInt32; - - /// Converts a signed integer with infinite precision to a 64-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toInt64(123_456_789) == (123_456_789 : Int64); - /// ``` - public let toInt64 : (self : Int) -> Int64 = Prim.intToInt64; - - /// Converts an 8-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromInt8(123 : Int8) == 123; - /// ``` - public let fromInt8 : (x : Int8) -> Int = Prim.int8ToInt; - - /// Converts a 16-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromInt16(12_345 : Int16) == 12_345; - /// ``` - public let fromInt16 : (x : Int16) -> Int = Prim.int16ToInt; - - /// Converts a 32-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromInt32(123_456 : Int32) == 123_456; - /// ``` - public let fromInt32 : (x : Int32) -> Int = Prim.int32ToInt; - - /// Converts a 64-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromInt64(123_456_789 : Int64) == 123_456_789; - /// ``` - public let fromInt64 : (x : Int64) -> Int = Prim.int64ToInt; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.min(2, -3) == -3; - /// ``` - public func min(x : Int, y : Int) : Int { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.max(2, -3) == 2; - /// ``` - public func max(x : Int, y : Int) : Int { - if (x < y) { y } else { x } - }; - - /// Equality function for Int types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int = 1; - /// let b : Int = -1; - /// assert not Int.equal(a, b); - /// ``` - public func equal(x : Int, y : Int) : Bool { x == y }; - - /// Inequality function for Int types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int, y : Int) : Bool { x != y }; - - /// "Less than" function for Int types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int, y : Int) : Bool { x < y }; - - /// "Less than or equal" function for Int types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.lessOrEqual(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int, y : Int) : Bool { x <= y }; - - /// "Greater than" function for Int types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.greater(1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int, y : Int) : Bool { x > y }; - - /// "Greater than or equal" function for Int types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.greaterOrEqual(1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int, y : Int) : Bool { x >= y }; - - /// General-purpose comparison function for `Int`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3], Int.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int, y : Int) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x` . - /// - /// Example: - /// ```motoko include=import - /// assert Int.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int) : Int { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.add(1, -2) == -1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int.add) == -4; - /// ``` - public func add(x : Int, y : Int) : Int { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.sub(1, 2) == -1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int.sub) == 4; - /// ``` - public func sub(x : Int, y : Int) : Int { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.mul(-2, 3) == -6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int.mul) == 6; - /// ``` - public func mul(x : Int, y : Int) : Int { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int.div(6, -2) == -3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int, y : Int) : Int { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int.rem(6, -4) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int, y : Int) : Int { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps when `y` is negative or `y > 2 ** 32 - 1`. - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.pow(-2, 3) == -8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int, y : Int) : Int { x ** y }; - - /// Returns an iterator over the integers from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int, toExclusive : Int) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int { - if (n >= toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int` values from the first to second argument with an exclusive upper bound, - /// incrementing by the specified step size. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// // Positive step - /// let iter1 = Int.rangeBy(1, 7, 2); - /// assert iter1.next() == ?1; - /// assert iter1.next() == ?3; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// // Negative step - /// let iter2 = Int.rangeBy(7, 1, -2); - /// assert iter2.next() == ?7; - /// assert iter2.next() == ?5; - /// assert iter2.next() == ?3; - /// assert iter2.next() == null; - /// ``` - /// - /// If `step` is 0 or if the iteration would not progress towards the bound, returns an empty iterator. - public func rangeBy(fromInclusive : Int, toExclusive : Int, step : Int) : Iter.Iter { - if (step == 0) { - Iter.empty() - } else if (step > 0 and fromInclusive < toExclusive) { - object { - var n = fromInclusive; - public func next() : ?Int { - if (n >= toExclusive) { - null - } else { - let current = n; - n += step; - ?current - } - } - } - } else if (step < 0 and fromInclusive > toExclusive) { - object { - var n = fromInclusive; - public func next() : ?Int { - if (n <= toExclusive) { - null - } else { - let current = n; - n += step; - ?current - } - } - } - } else { - Iter.empty() - } - }; - - /// Returns an iterator over the integers from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int.rangeInclusive(3, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int, to : Int) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - public func next() : ?Int { - if (n > to) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over the integers from the first to second argument, inclusive, - /// incrementing by the specified step size. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// // Positive step - /// let iter1 = Int.rangeByInclusive(1, 7, 2); - /// assert iter1.next() == ?1; - /// assert iter1.next() == ?3; - /// assert iter1.next() == ?5; - /// assert iter1.next() == ?7; - /// assert iter1.next() == null; - /// - /// // Negative step - /// let iter2 = Int.rangeByInclusive(7, 1, -2); - /// assert iter2.next() == ?7; - /// assert iter2.next() == ?5; - /// assert iter2.next() == ?3; - /// assert iter2.next() == ?1; - /// assert iter2.next() == null; - /// ``` - /// - /// If `from == to`, return an iterator which only returns that value. - /// - /// Otherwise, if `step` is 0 or if the iteration would not progress towards the bound, returns an empty iterator. - public func rangeByInclusive(from : Int, to : Int, step : Int) : Iter.Iter { - if (from == to) { - Iter.singleton(from) - } else if (step == 0) { - Iter.empty() - } else if (step > 0 and from < to) { - object { - var n = from; - public func next() : ?Int { - if (n >= to + 1) { - null - } else { - let current = n; - n += step; - ?current - } - } - } - } else if (step < 0 and from > to) { - object { - var n = from; - public func next() : ?Int { - if (n + 1 <= to) { - null - } else { - let current = n; - n += step; - ?current - } - } - } - } else { - Iter.empty() - } - }; - -} diff --git a/.mops/core@2.3.1/src/Int16.mo b/.mops/core@2.3.1/src/Int16.mo deleted file mode 100644 index 40b3b6d..0000000 --- a/.mops/core@2.3.1/src/Int16.mo +++ /dev/null @@ -1,774 +0,0 @@ -/// Utility functions on 16-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int16 "mo:core/Int16"; -/// ``` - -import Int "Int"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 16-bit signed integers. - public type Int16 = Prim.Types.Int16; - - /// Minimum 16-bit integer value, `-2 ** 15`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.minValue == (-32_768 : Int16); - /// ``` - public let minValue : Int16 = -32_768; - - /// Maximum 16-bit integer value, `+2 ** 15 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.maxValue == (+32_767 : Int16); - /// ``` - public let maxValue : Int16 = 32_767; - - /// Converts a 16-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toInt(12_345) == (12_345 : Int); - /// ``` - public let toInt : (self : Int16) -> Int = Prim.int16ToInt; - - /// Converts a signed integer with infinite precision to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromInt(12_345) == (+12_345 : Int16); - /// ``` - public let fromInt : Int -> Int16 = Prim.intToInt16; - - /// Converts a signed integer with infinite precision to a 16-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromIntWrap(-12_345) == (-12_345 : Int); - /// ``` - public let fromIntWrap : Int -> Int16 = Prim.intToInt16Wrap; - - /// Converts a 8-bit signed integer to a 16-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromInt8(-123) == (-123 : Int16); - /// ``` - public let fromInt8 : Int8 -> Int16 = Prim.int8ToInt16; - - /// Converts a 16-bit signed integer to a 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toInt8(-123) == (-123 : Int8); - /// ``` - public let toInt8 : (self : Int16) -> Int8 = Prim.int16ToInt8; - - /// Converts a 32-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromInt32(-12_345) == (-12_345 : Int16); - /// ``` - public let fromInt32 : Int32 -> Int16 = Prim.int32ToInt16; - - /// Converts a 16-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toInt32(-12_345) == (-12_345 : Int32); - /// ``` - public let toInt32 : (self : Int16) -> Int32 = Prim.int16ToInt32; - - /// Converts a 64-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromInt64(-12_345) == (-12_345 : Int16); - /// ``` - public func fromInt64(x : Int64) : Int16 { - Prim.int32ToInt16(Prim.int64ToInt32(x)) - }; - - /// Converts a 16-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toInt64(-12_345) == (-12_345 : Int64); - /// ``` - public func toInt64(self : Int16) : Int64 { - Prim.int32ToInt64(Prim.int16ToInt32(self)) - }; - - /// Converts an unsigned 16-bit integer to a signed 16-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromNat16(12_345) == (+12_345 : Int16); - /// ``` - public let fromNat16 : Nat16 -> Int16 = Prim.nat16ToInt16; - - /// Converts a signed 16-bit integer to an unsigned 16-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toNat16(-1) == (65_535 : Nat16); // underflow - /// ``` - public let toNat16 : (self : Int16) -> Nat16 = Prim.int16ToNat16; - - /// Returns the Text representation of `x`. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toText(-12345) == "-12345"; - /// ``` - public func toText(self : Int16) : Text { - Int.toText(toInt(self)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 15` (the minimum `Int16` value). - /// - /// Example: - /// ```motoko include=import - /// assert Int16.abs(-12345) == +12_345; - /// ``` - public func abs(x : Int16) : Int16 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.min(+2, -3) == -3; - /// ``` - public func min(x : Int16, y : Int16) : Int16 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.max(+2, -3) == +2; - /// ``` - public func max(x : Int16, y : Int16) : Int16 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int16 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int16 = -123; - /// let b : Int16 = 123; - /// assert not Int16.equal(a, b); - /// ``` - public func equal(x : Int16, y : Int16) : Bool { x == y }; - - /// Inequality function for Int16 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int16, y : Int16) : Bool { x != y }; - - /// "Less than" function for Int16 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int16, y : Int16) : Bool { x < y }; - - /// "Less than or equal" function for Int16 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.lessOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int16, y : Int16) : Bool { x <= y }; - - /// "Greater than" function for Int16 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert not Int16.greater(-2, 1); - /// ``` - public func greater(x : Int16, y : Int16) : Bool { x > y }; - - /// "Greater than or equal" function for Int16 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.greaterOrEqual(-2, -2); - /// ``` - public func greaterOrEqual(x : Int16, y : Int16) : Bool { - x >= y - }; - - /// General-purpose comparison function for `Int16`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3] : [Int16], Int16.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int16, y : Int16) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 15)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int16) : Int16 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.add(100, 23) == +123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int16.add) == -4; - /// ``` - public func add(x : Int16, y : Int16) : Int16 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.sub(123, 100) == +23; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int16.sub) == 4; - /// ``` - public func sub(x : Int16, y : Int16) : Int16 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.mul(12, 10) == +120; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int16.mul) == 6; - /// ``` - public func mul(x : Int16, y : Int16) : Int16 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.div(123, 10) == +12; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int16, y : Int16) : Int16 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.rem(123, 10) == +3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int16, y : Int16) : Int16 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 16`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.pow(2, 10) == +1_024; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int16, y : Int16) : Int16 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitnot(-256 /* 0xff00 */) == +255 // 0xff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int16) : Int16 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitand(0x0fff, 0x00f0) == +240 // 0xf0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int16, y : Int16) : Int16 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitor(0x0f0f, 0x00f0) == +4_095 // 0x0fff; - /// ``` - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int16, y : Int16) : Int16 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitxor(0x0fff, 0x00f0) == +3_855 // 0x0f0f; - /// ``` - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int16, y : Int16) : Int16 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 16`, the semantics is the same as for `bitshiftLeft(x, y % 16)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitshiftLeft(1, 8) == +256 // 0x100 equivalent to `2 ** 8`.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int16, y : Int16) : Int16 { - x << y - }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 16`, the semantics is the same as for `bitshiftRight(x, y % 16)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitshiftRight(1024, 8) == +4 // equivalent to `1024 / (2 ** 8)`; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int16, y : Int16) : Int16 { - x >> y - }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 16`, the semantics is the same as for `bitrotLeft(x, y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitrotLeft(0x2001, 4) == +18 // 0x12.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int16, y : Int16) : Int16 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 16`, the semantics is the same as for `bitrotRight(x, y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitrotRight(0x2010, 8) == +4_128 // 0x01020.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int16, y : Int16) : Int16 { - x <>> y - }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 16`, the semantics is the same as for `bittest(x, p % 16)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bittest(128, 7); - /// ``` - public func bittest(x : Int16, p : Nat) : Bool { - Prim.btstInt16(x, Prim.intToInt16(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 16`, the semantics is the same as for `bitset(x, p % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitset(0, 7) == +128; - /// ``` - public func bitset(x : Int16, p : Nat) : Int16 { - x | (1 << Prim.intToInt16(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 16`, the semantics is the same as for `bitclear(x, p % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitclear(-1, 7) == -129; - /// ``` - public func bitclear(x : Int16, p : Nat) : Int16 { - x & ^(1 << Prim.intToInt16(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 16`, the semantics is the same as for `bitclear(x, p % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitflip(255, 7) == +127; - /// ``` - public func bitflip(x : Int16, p : Nat) : Int16 { - x ^ (1 << Prim.intToInt16(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitcountNonZero(0xff) == +8; - /// ``` - public let bitcountNonZero : (x : Int16) -> Int16 = Prim.popcntInt16; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitcountLeadingZero(0x80) == +8; - /// ``` - public let bitcountLeadingZero : (x : Int16) -> Int16 = Prim.clzInt16; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitcountTrailingZero(0x0100) == +8; - /// ``` - public let bitcountTrailingZero : (x : Int16) -> Int16 = Prim.ctzInt16; - - /// Returns the upper (i.e. most significant) and lower (least significant) byte of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.explode 0x77ee == (119, 238); - /// ``` - public let explode : (x : Int16) -> (msb : Nat8, lsb : Nat8) = Prim.explodeInt16; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.addWrap(2 ** 14, 2 ** 14) == -32_768; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int16, y : Int16) : Int16 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.subWrap(-2 ** 15, 1) == +32_767; // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int16, y : Int16) : Int16 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.mulWrap(2 ** 8, 2 ** 8) == 0; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int16, y : Int16) : Int16 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 16`. - /// - /// Example: - /// ```motoko include=import - /// - /// assert Int16.powWrap(2, 15) == -32_768; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int16, y : Int16) : Int16 { x **% y }; - - /// Returns an iterator over `Int16` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int16, toExclusive : Int16) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int16 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int16` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int16, to : Int16) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Int16 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Int16 values, from minValue to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.allValues(); - /// assert iter.next() == ?-32_768; - /// assert iter.next() == ?-32_767; - /// assert iter.next() == ?-32_766; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(minValue, maxValue) - }; - -} diff --git a/.mops/core@2.3.1/src/Int32.mo b/.mops/core@2.3.1/src/Int32.mo deleted file mode 100644 index 947b76b..0000000 --- a/.mops/core@2.3.1/src/Int32.mo +++ /dev/null @@ -1,787 +0,0 @@ -/// Utility functions on 32-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int32 "mo:core/Int32"; -/// ``` -import Int "Int"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 32-bit signed integers. - public type Int32 = Prim.Types.Int32; - - /// Minimum 32-bit integer value, `-2 ** 31`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.minValue == -2_147_483_648; - /// ``` - public let minValue : Int32 = -2_147_483_648; - - /// Maximum 32-bit integer value, `+2 ** 31 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.maxValue == +2_147_483_647; - /// ``` - public let maxValue : Int32 = 2_147_483_647; - - /// Converts a 32-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toInt(123_456) == (123_456 : Int); - /// ``` - public let toInt : (self : Int32) -> Int = Prim.int32ToInt; - - /// Converts a signed integer with infinite precision to a 32-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromInt(123_456) == (+123_456 : Int32); - /// ``` - public let fromInt : Int -> Int32 = Prim.intToInt32; - - /// Converts a signed integer with infinite precision to a 32-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromIntWrap(-123_456) == (-123_456 : Int); - /// ``` - public let fromIntWrap : Int -> Int32 = Prim.intToInt32Wrap; - - /// Converts a 16-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromInt16(-123) == (-123 : Int32); - /// ``` - public let fromInt16 : Int16 -> Int32 = Prim.int16ToInt32; - - /// Converts an 8-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromInt8(-123) == (-123 : Int32); - /// ``` - public func fromInt8(x : Int8) : Int32 { - Prim.int16ToInt32(Prim.int8ToInt16(x)) - }; - - /// Converts a 32-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toInt8(-123) == (-123 : Int8); - /// ``` - public func toInt8(self : Int32) : Int8 { - Prim.int16ToInt8(Prim.int32ToInt16(self)) - }; - - /// Converts a 32-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toInt16(-123) == (-123 : Int16); - /// ``` - public func toInt16(self : Int32) : Int16 { - Prim.int32ToInt16(self) - }; - - /// Converts a 64-bit signed integer to a 32-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromInt64(-123_456) == (-123_456 : Int32); - /// ``` - public let fromInt64 : Int64 -> Int32 = Prim.int64ToInt32; - - /// Converts a 32-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toInt64(-123_456) == (-123_456 : Int64); - /// ``` - public let toInt64 : (self : Int32) -> Int64 = Prim.int32ToInt64; - - /// Converts an unsigned 32-bit integer to a signed 32-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromNat32(123_456) == (+123_456 : Int32); - /// ``` - public let fromNat32 : Nat32 -> Int32 = Prim.nat32ToInt32; - - /// Converts a signed 32-bit integer to an unsigned 32-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toNat32(-1) == (4_294_967_295 : Nat32); // underflow - /// ``` - public let toNat32 : (self : Int32) -> Nat32 = Prim.int32ToNat32; - - /// Returns the Text representation of `x`. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toText(-123456) == "-123456"; - /// ``` - public func toText(self : Int32) : Text { - Int.toText(toInt(self)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 31` (the minimum `Int32` value). - /// - /// Example: - /// ```motoko include=import - /// assert Int32.abs(-123456) == +123_456; - /// ``` - public func abs(x : Int32) : Int32 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.min(+2, -3) == -3; - /// ``` - public func min(x : Int32, y : Int32) : Int32 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.max(+2, -3) == +2; - /// ``` - public func max(x : Int32, y : Int32) : Int32 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int32 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int32 = -123; - /// let b : Int32 = 123; - /// assert not Int32.equal(a, b); - /// ``` - public func equal(x : Int32, y : Int32) : Bool { x == y }; - - /// Inequality function for Int32 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int32, y : Int32) : Bool { x != y }; - - /// "Less than" function for Int32 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int32, y : Int32) : Bool { x < y }; - - /// "Less than or equal" function for Int32 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.lessOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int32, y : Int32) : Bool { x <= y }; - - /// "Greater than" function for Int32 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.greater(-2, -3); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int32, y : Int32) : Bool { x > y }; - - /// "Greater than or equal" function for Int32 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.greaterOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int32, y : Int32) : Bool { - x >= y - }; - - /// General-purpose comparison function for `Int32`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3] : [Int32], Int32.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int32, y : Int32) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 31)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int32) : Int32 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.add(100, 23) == +123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int32.add) == -4; - /// ``` - public func add(x : Int32, y : Int32) : Int32 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.sub(1234, 123) == +1_111; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int32.sub) == 4; - /// ``` - public func sub(x : Int32, y : Int32) : Int32 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.mul(123, 100) == +12_300; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int32.mul) == 6; - /// ``` - public func mul(x : Int32, y : Int32) : Int32 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.div(123, 10) == +12; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int32, y : Int32) : Int32 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.rem(123, 10) == +3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int32, y : Int32) : Int32 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 32`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.pow(2, 10) == +1_024; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int32, y : Int32) : Int32 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitnot(-256 /* 0xffff_ff00 */) == +255 // 0xff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int32) : Int32 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitand(0xffff, 0x00f0) == +240 // 0xf0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int32, y : Int32) : Int32 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitor(0xffff, 0x00f0) == +65_535 // 0xffff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int32, y : Int32) : Int32 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitxor(0xffff, 0x00f0) == +65_295 // 0xff0f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int32, y : Int32) : Int32 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 32`, the semantics is the same as for `bitshiftLeft(x, y % 32)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitshiftLeft(1, 8) == +256 // 0x100 equivalent to `2 ** 8`.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int32, y : Int32) : Int32 { - x << y - }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 32`, the semantics is the same as for `bitshiftRight(x, y % 32)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitshiftRight(1024, 8) == +4 // equivalent to `1024 / (2 ** 8)`; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int32, y : Int32) : Int32 { - x >> y - }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 32`, the semantics is the same as for `bitrotLeft(x, y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitrotLeft(0x2000_0001, 4) == +18 // 0x12.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int32, y : Int32) : Int32 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 32`, the semantics is the same as for `bitrotRight(x, y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitrotRight(0x0002_0001, 8) == +16_777_728 // 0x0100_0200.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int32, y : Int32) : Int32 { - x <>> y - }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 32`, the semantics is the same as for `bittest(x, p % 32)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bittest(128, 7); - /// ``` - public func bittest(x : Int32, p : Nat) : Bool { - Prim.btstInt32(x, Prim.intToInt32(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 32`, the semantics is the same as for `bitset(x, p % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitset(0, 7) == +128; - /// ``` - public func bitset(x : Int32, p : Nat) : Int32 { - x | (1 << Prim.intToInt32(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 32`, the semantics is the same as for `bitclear(x, p % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitclear(-1, 7) == -129; - /// ``` - public func bitclear(x : Int32, p : Nat) : Int32 { - x & ^(1 << Prim.intToInt32(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 32`, the semantics is the same as for `bitclear(x, p % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitflip(255, 7) == +127; - /// ``` - public func bitflip(x : Int32, p : Nat) : Int32 { - x ^ (1 << Prim.intToInt32(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitcountNonZero(0xffff) == +16; - /// ``` - public let bitcountNonZero : (x : Int32) -> Int32 = Prim.popcntInt32; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitcountLeadingZero(0x8000) == +16; - /// ``` - public let bitcountLeadingZero : (x : Int32) -> Int32 = Prim.clzInt32; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitcountTrailingZero(0x0201_0000) == +16; - /// ``` - public let bitcountTrailingZero : (x : Int32) -> Int32 = Prim.ctzInt32; - - /// Returns the upper (i.e. most significant), lower (least significant) - /// and in-between bytes of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.explode 0x66885511 == (102, 136, 85, 17); - /// ``` - public let explode : (x : Int32) -> (msb : Nat8, Nat8, Nat8, lsb : Nat8) = Prim.explodeInt32; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.addWrap(2 ** 30, 2 ** 30) == -2_147_483_648; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int32, y : Int32) : Int32 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.subWrap(-2 ** 31, 1) == +2_147_483_647; // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int32, y : Int32) : Int32 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.mulWrap(2 ** 16, 2 ** 16) == 0; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int32, y : Int32) : Int32 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 32`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.powWrap(2, 31) == -2_147_483_648; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int32, y : Int32) : Int32 { x **% y }; - - /// Returns an iterator over `Int32` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int32, toExclusive : Int32) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int32 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int32` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int32, to : Int32) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Int32 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Int32 values, from minValue to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.allValues(); - /// assert iter.next() == ?-2_147_483_648; - /// assert iter.next() == ?-2_147_483_647; - /// assert iter.next() == ?-2_147_483_646; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(minValue, maxValue) - }; - -} diff --git a/.mops/core@2.3.1/src/Int64.mo b/.mops/core@2.3.1/src/Int64.mo deleted file mode 100644 index 95f5647..0000000 --- a/.mops/core@2.3.1/src/Int64.mo +++ /dev/null @@ -1,796 +0,0 @@ -/// Utility functions on 64-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int64 "mo:core/Int64"; -/// ``` - -import Int "Int"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 64-bit signed integers. - public type Int64 = Prim.Types.Int64; - - /// Minimum 64-bit integer value, `-2 ** 63`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.minValue == -9_223_372_036_854_775_808; - /// ``` - public let minValue : Int64 = -9_223_372_036_854_775_808; - - /// Maximum 64-bit integer value, `+2 ** 63 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.maxValue == +9_223_372_036_854_775_807; - /// ``` - public let maxValue : Int64 = 9_223_372_036_854_775_807; - - /// Converts a 64-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toInt(123_456) == (123_456 : Int); - /// ``` - public let toInt : (self : Int64) -> Int = Prim.int64ToInt; - - /// Converts a signed integer with infinite precision to a 64-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromInt(123_456) == (+123_456 : Int64); - /// ``` - public let fromInt : (x : Int) -> Int64 = Prim.intToInt64; - - /// Converts a 32-bit signed integer to a 64-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromInt32(-123_456) == (-123_456 : Int64); - /// ``` - public let fromInt32 : (x : Int32) -> Int64 = Prim.int32ToInt64; - - /// Converts a 16-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromInt16(-123) == (-123 : Int64); - /// ``` - public func fromInt16(x : Int16) : Int64 { - Prim.int32ToInt64(Prim.int16ToInt32(x)) - }; - - /// Converts an 8-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromInt8(-123) == (-123 : Int64); - /// ``` - public func fromInt8(x : Int8) : Int64 { - Prim.int32ToInt64(Prim.int16ToInt32(Prim.int8ToInt16(x))) - }; - - /// Converts a 64-bit signed integer to a 32-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toInt32(-123_456) == (-123_456 : Int32); - /// ``` - public func toInt32(self : Int64) : Int32 { - Prim.int64ToInt32(self) - }; - - /// Converts a 64-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toInt16(-123) == (-123 : Int16); - /// ``` - public func toInt16(self : Int64) : Int16 { - Prim.int32ToInt16(Prim.int64ToInt32(self)) - }; - - /// Converts a 64-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toInt8(-123) == (-123 : Int8); - /// ``` - public func toInt8(self : Int64) : Int8 { - Prim.int16ToInt8(Prim.int32ToInt16(Prim.int64ToInt32(self))) - }; - - /// Converts a signed integer with infinite precision to a 64-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromIntWrap(-123_456) == (-123_456 : Int64); - /// ``` - public let fromIntWrap : Int -> Int64 = Prim.intToInt64Wrap; - - /// Converts an unsigned 64-bit integer to a signed 64-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromNat64(123_456) == (+123_456 : Int64); - /// ``` - public let fromNat64 : Nat64 -> Int64 = Prim.nat64ToInt64; - - /// Converts a signed 64-bit integer to an unsigned 64-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toNat64(-1) == (18_446_744_073_709_551_615 : Nat64); // underflow - /// ``` - public let toNat64 : (self : Int64) -> Nat64 = Prim.int64ToNat64; - - /// Returns the Text representation of `x`. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toText(-123456) == "-123456"; - /// ``` - public func toText(self : Int64) : Text { - Int.toText(toInt(self)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 63` (the minimum `Int64` value). - /// - /// Example: - /// ```motoko include=import - /// assert Int64.abs(-123456) == +123_456; - /// ``` - public func abs(x : Int64) : Int64 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.min(+2, -3) == -3; - /// ``` - public func min(x : Int64, y : Int64) : Int64 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.max(+2, -3) == +2; - /// ``` - public func max(x : Int64, y : Int64) : Int64 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int64 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int64 = -123; - /// let b : Int64 = 123; - /// assert not Int64.equal(a, b); - /// ``` - public func equal(x : Int64, y : Int64) : Bool { x == y }; - - /// Inequality function for Int64 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int64, y : Int64) : Bool { x != y }; - - /// "Less than" function for Int64 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int64, y : Int64) : Bool { x < y }; - - /// "Less than or equal" function for Int64 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.lessOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int64, y : Int64) : Bool { x <= y }; - - /// "Greater than" function for Int64 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.greater(-2, -3); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int64, y : Int64) : Bool { x > y }; - - /// "Greater than or equal" function for Int64 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.greaterOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int64, y : Int64) : Bool { - x >= y - }; - - /// General-purpose comparison function for `Int64`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3] : [Int64], Int64.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int64, y : Int64) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 63)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int64) : Int64 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.add(1234, 123) == +1_357; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int64.add) == -4; - /// ``` - public func add(x : Int64, y : Int64) : Int64 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.sub(123, 100) == +23; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int64.sub) == 4; - /// ``` - public func sub(x : Int64, y : Int64) : Int64 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.mul(123, 10) == +1_230; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int64.mul) == 6; - /// ``` - public func mul(x : Int64, y : Int64) : Int64 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.div(123, 10) == +12; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int64, y : Int64) : Int64 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.rem(123, 10) == +3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int64, y : Int64) : Int64 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 64`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.pow(2, 10) == +1_024; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int64, y : Int64) : Int64 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitnot(-256 /* 0xffff_ffff_ffff_ff00 */) == +255 // 0xff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int64) : Int64 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitand(0xffff, 0x00f0) == +240 // 0xf0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int64, y : Int64) : Int64 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitor(0xffff, 0x00f0) == +65_535 // 0xffff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int64, y : Int64) : Int64 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitxor(0xffff, 0x00f0) == +65_295 // 0xff0f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int64, y : Int64) : Int64 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 64`, the semantics is the same as for `bitshiftLeft(x, y % 64)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitshiftLeft(1, 8) == +256 // 0x100 equivalent to `2 ** 8`.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int64, y : Int64) : Int64 { - x << y - }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 64`, the semantics is the same as for `bitshiftRight(x, y % 64)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitshiftRight(1024, 8) == +4 // equivalent to `1024 / (2 ** 8)`; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int64, y : Int64) : Int64 { - x >> y - }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 64`, the semantics is the same as for `bitrotLeft(x, y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// - /// assert Int64.bitrotLeft(0x2000_0000_0000_0001, 4) == +18 // 0x12.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int64, y : Int64) : Int64 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 64`, the semantics is the same as for `bitrotRight(x, y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitrotRight(0x0002_0000_0000_0001, 48) == +65538 // 0x1_0002.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int64, y : Int64) : Int64 { - x <>> y - }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 64`, the semantics is the same as for `bittest(x, p % 64)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bittest(128, 7); - /// ``` - public func bittest(x : Int64, p : Nat) : Bool { - Prim.btstInt64(x, Prim.intToInt64(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 64`, the semantics is the same as for `bitset(x, p % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitset(0, 7) == +128; - /// ``` - public func bitset(x : Int64, p : Nat) : Int64 { - x | (1 << Prim.intToInt64(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 64`, the semantics is the same as for `bitclear(x, p % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitclear(-1, 7) == -129; - /// ``` - public func bitclear(x : Int64, p : Nat) : Int64 { - x & ^(1 << Prim.intToInt64(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 64`, the semantics is the same as for `bitclear(x, p % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitflip(255, 7) == +127; - /// ``` - public func bitflip(x : Int64, p : Nat) : Int64 { - x ^ (1 << Prim.intToInt64(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitcountNonZero(0xffff) == +16; - /// ``` - public let bitcountNonZero : (x : Int64) -> Int64 = Prim.popcntInt64; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitcountLeadingZero(0x8000_0000) == +32; - /// ``` - public let bitcountLeadingZero : (x : Int64) -> Int64 = Prim.clzInt64; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitcountTrailingZero(0x0201_0000) == +16; - /// ``` - public let bitcountTrailingZero : (x : Int64) -> Int64 = Prim.ctzInt64; - - /// Returns the upper (i.e. most significant), lower (least significant) - /// and in-between bytes of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.explode 0x33772266aa885511 == (51, 119, 34, 102, 170, 136, 85, 17); - /// ``` - public let explode : (x : Int64) -> (msb : Nat8, Nat8, Nat8, Nat8, Nat8, Nat8, Nat8, lsb : Nat8) = Prim.explodeInt64; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.addWrap(2 ** 62, 2 ** 62) == -9_223_372_036_854_775_808; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int64, y : Int64) : Int64 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.subWrap(-2 ** 63, 1) == +9_223_372_036_854_775_807; // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int64, y : Int64) : Int64 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.mulWrap(2 ** 32, 2 ** 32) == 0; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int64, y : Int64) : Int64 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 64`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.powWrap(2, 63) == -9_223_372_036_854_775_808; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int64, y : Int64) : Int64 { x **% y }; - - /// Returns an iterator over `Int64` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int64, toExclusive : Int64) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int64 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int64` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int64, to : Int64) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Int64 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Int64 values, from minValue to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.allValues(); - /// assert iter.next() == ?-9_223_372_036_854_775_808; - /// assert iter.next() == ?-9_223_372_036_854_775_807; - /// assert iter.next() == ?-9_223_372_036_854_775_806; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(minValue, maxValue) - }; - -} diff --git a/.mops/core@2.3.1/src/Int8.mo b/.mops/core@2.3.1/src/Int8.mo deleted file mode 100644 index ffde265..0000000 --- a/.mops/core@2.3.1/src/Int8.mo +++ /dev/null @@ -1,771 +0,0 @@ -/// Utility functions on 8-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int8 "mo:core/Int8"; -/// ``` -import Int "Int"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 8-bit signed integers. - public type Int8 = Prim.Types.Int8; - - /// Minimum 8-bit integer value, `-2 ** 7`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.minValue == -128; - /// ``` - public let minValue : Int8 = -128; - - /// Maximum 8-bit integer value, `+2 ** 7 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.maxValue == +127; - /// ``` - public let maxValue : Int8 = 127; - - /// Converts an 8-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toInt(123) == (123 : Int); - /// ``` - public let toInt : (self : Int8) -> Int = Prim.int8ToInt; - - /// Converts a signed integer with infinite precision to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromInt(123) == (+123 : Int8); - /// ``` - public let fromInt : Int -> Int8 = Prim.intToInt8; - - /// Converts a signed integer with infinite precision to an 8-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromIntWrap(-123) == (-123 : Int8); - /// ``` - public let fromIntWrap : Int -> Int8 = Prim.intToInt8Wrap; - - /// Converts a 16-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromInt16(123) == (+123 : Int8); - /// ``` - public let fromInt16 : Int16 -> Int8 = Prim.int16ToInt8; - - /// Converts an 8-bit signed integer to a 16-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toInt16(123) == (+123 : Int16); - /// ``` - public let toInt16 : (self : Int8) -> Int16 = Prim.int8ToInt16; - - /// Converts a 32-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromInt32(123) == (+123 : Int8); - /// ``` - public func fromInt32(x : Int32) : Int8 { - Prim.int16ToInt8(Prim.int32ToInt16(x)) - }; - - /// Converts an 8-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toInt32(123) == (+123 : Int32); - /// ``` - public func toInt32(self : Int8) : Int32 { - Prim.int16ToInt32(Prim.int8ToInt16(self)) - }; - - /// Converts a 64-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromInt64(123) == (+123 : Int8); - /// ``` - public func fromInt64(x : Int64) : Int8 { - Prim.int16ToInt8(Prim.int32ToInt16(Prim.int64ToInt32(x))) - }; - - /// Converts an 8-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toInt64(123) == (+123 : Int64); - /// ``` - public func toInt64(self : Int8) : Int64 { - Prim.int32ToInt64(Prim.int16ToInt32(Prim.int8ToInt16(self))) - }; - - /// Converts an unsigned 8-bit integer to a signed 8-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromNat8(123) == (+123 : Int8); - /// ``` - public let fromNat8 : Nat8 -> Int8 = Prim.nat8ToInt8; - - /// Converts a signed 8-bit integer to an unsigned 8-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toNat8(-1) == (255 : Nat8); // underflow - /// ``` - public let toNat8 : (self : Int8) -> Nat8 = Prim.int8ToNat8; - - /// Converts an integer number to its textual representation. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toText(-123) == "-123"; - /// ``` - public func toText(self : Int8) : Text { - Int.toText(toInt(self)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 7` (the minimum `Int8` value). - /// - /// Example: - /// ```motoko include=import - /// assert Int8.abs(-123) == +123; - /// ``` - public func abs(x : Int8) : Int8 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.min(+2, -3) == -3; - /// ``` - public func min(x : Int8, y : Int8) : Int8 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.max(+2, -3) == +2; - /// ``` - public func max(x : Int8, y : Int8) : Int8 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int8 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int8 = -123; - /// let b : Int8 = 123; - /// assert not Int8.equal(a, b); - /// ``` - public func equal(x : Int8, y : Int8) : Bool { x == y }; - - /// Inequality function for Int8 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int8, y : Int8) : Bool { x != y }; - - /// "Less than" function for Int8 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int8, y : Int8) : Bool { x < y }; - - /// "Less than or equal" function for Int8 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.lessOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int8, y : Int8) : Bool { x <= y }; - - /// "Greater than" function for Int8 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.greater(-2, -3); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int8, y : Int8) : Bool { x > y }; - - /// "Greater than or equal" function for Int8 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.greaterOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int8, y : Int8) : Bool { x >= y }; - - /// General-purpose comparison function for `Int8`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3] : [Int8], Int8.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int8, y : Int8) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 7)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int8) : Int8 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.add(100, 23) == +123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int8.add) == -4; - /// ``` - public func add(x : Int8, y : Int8) : Int8 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.sub(123, 23) == +100; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int8.sub) == 4; - /// ``` - public func sub(x : Int8, y : Int8) : Int8 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.mul(12, 10) == +120; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int8.mul) == 6; - /// ``` - public func mul(x : Int8, y : Int8) : Int8 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.div(123, 10) == +12; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int8, y : Int8) : Int8 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.rem(123, 10) == +3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int8, y : Int8) : Int8 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 8`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.pow(2, 6) == +64; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int8, y : Int8) : Int8 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitnot(-16 /* 0xf0 */) == +15 // 0x0f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int8) : Int8 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitand(0x1f, 0x70) == +16 // 0x10; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int8, y : Int8) : Int8 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitor(0x0f, 0x70) == +127 // 0x7f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int8, y : Int8) : Int8 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitxor(0x70, 0x7f) == +15 // 0x0f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int8, y : Int8) : Int8 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 8`, the semantics is the same as for `bitshiftLeft(x, y % 8)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitshiftLeft(1, 4) == +16 // 0x10 equivalent to `2 ** 4`.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int8, y : Int8) : Int8 { x << y }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 8`, the semantics is the same as for `bitshiftRight(x, y % 8)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitshiftRight(64, 4) == +4 // equivalent to `64 / (2 ** 4)`; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int8, y : Int8) : Int8 { x >> y }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 8`, the semantics is the same as for `bitrotLeft(x, y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitrotLeft(0x11 /* 0b0001_0001 */, 2) == +68 // 0b0100_0100 == 0x44.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int8, y : Int8) : Int8 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 8`, the semantics is the same as for `bitrotRight(x, y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitrotRight(0x11 /* 0b0001_0001 */, 1) == -120 // 0b1000_1000 == 0x88.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int8, y : Int8) : Int8 { x <>> y }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 8`, the semantics is the same as for `bittest(x, p % 8)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bittest(64, 6); - /// ``` - public func bittest(x : Int8, p : Nat) : Bool { - Prim.btstInt8(x, Prim.intToInt8(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 8`, the semantics is the same as for `bitset(x, p % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitset(0, 6) == +64; - /// ``` - public func bitset(x : Int8, p : Nat) : Int8 { - x | (1 << Prim.intToInt8(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 8`, the semantics is the same as for `bitclear(x, p % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitclear(-1, 6) == -65; - /// ``` - public func bitclear(x : Int8, p : Nat) : Int8 { - x & ^(1 << Prim.intToInt8(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 8`, the semantics is the same as for `bitclear(x, p % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitflip(127, 6) == +63; - /// ``` - public func bitflip(x : Int8, p : Nat) : Int8 { - x ^ (1 << Prim.intToInt8(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitcountNonZero(0x0f) == +4; - /// ``` - public let bitcountNonZero : (x : Int8) -> Int8 = Prim.popcntInt8; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitcountLeadingZero(0x08) == +4; - /// ``` - public let bitcountLeadingZero : (x : Int8) -> Int8 = Prim.clzInt8; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitcountTrailingZero(0x10) == +4; - /// ``` - public let bitcountTrailingZero : (x : Int8) -> Int8 = Prim.ctzInt8; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.addWrap(2 ** 6, 2 ** 6) == -128; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int8, y : Int8) : Int8 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.subWrap(-2 ** 7, 1) == +127; // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int8, y : Int8) : Int8 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.mulWrap(2 ** 4, 2 ** 4) == 0; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int8, y : Int8) : Int8 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 8`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.powWrap(2, 7) == -128; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int8, y : Int8) : Int8 { x **% y }; - - /// Returns an iterator over `Int8` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int8, toExclusive : Int8) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int8 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int8` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int8, to : Int8) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Int8 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Int8 values, from minValue to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.allValues(); - /// assert iter.next() == ?-128; - /// assert iter.next() == ?-127; - /// assert iter.next() == ?-126; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(minValue, maxValue) - }; - -} diff --git a/.mops/core@2.3.1/src/InternetComputer.mo b/.mops/core@2.3.1/src/InternetComputer.mo deleted file mode 100644 index 1a6d618..0000000 --- a/.mops/core@2.3.1/src/InternetComputer.mo +++ /dev/null @@ -1,101 +0,0 @@ -/// Low-level interface to the Internet Computer. - -import Prim "mo:⛔"; - -module { - - /// Calls `canister`'s update or query function, `name`, with the binary contents of `data` as IC argument. - /// Returns the response to the call, an IC _reply_ or _reject_, as a Motoko future: - /// - /// * The message data of an IC reply determines the binary contents of `reply`. - /// * The error code and textual message data of an IC reject determines the future's `Error` value. - /// - /// Note: `call` is an asynchronous function and can only be applied in an asynchronous context. - /// - /// Example: - /// ```motoko no-repl - /// import IC "mo:core/InternetComputer"; - /// import Principal "mo:core/Principal"; - /// - /// persistent actor { - /// type OutputType = { decimals : Nat32 }; - /// - /// public func example() : async ?OutputType { - /// let ledger = Principal.fromText("ryjl3-tyaaa-aaaaa-aaaba-cai"); - /// let method = "decimals"; - /// let input = (); - /// - /// let rawReply = await IC.call(ledger, method, to_candid (input)); // serialized Candid - /// let output : ?OutputType = from_candid (rawReply); - /// assert output == ?{ decimals = 8 }; - /// output - /// } - /// } - /// ``` - /// - /// [Learn more about Candid serialization](https://internetcomputer.org/docs/motoko/language-manual#candid-serialization) - public let call : (canister : Principal, name : Text, data : Blob) -> async (reply : Blob) = Prim.call_raw; - - /// `isReplicated` is true for update messages and for queries that passed through consensus. - public let isReplicated : () -> Bool = Prim.isReplicatedExecution; - - /// Given computation, `comp`, counts the number of actual and (for IC system calls) notional WebAssembly - /// instructions performed during the execution of `comp()`. - /// - /// More precisely, returns the difference between the state of the IC instruction counter (_performance counter_ `0`) before and after executing `comp()` - /// (see [Performance Counter](https://internetcomputer.org/docs/current/references/ic-interface-spec#system-api-performance-counter)). - /// - /// NB: `countInstructions(comp)` will _not_ account for any deferred garbage collection costs incurred by `comp()`. - /// - /// Example: - /// ```motoko no-repl - /// import IC "mo:core/InternetComputer"; - /// - /// let count = IC.countInstructions(func() { - /// // ... - /// }); - /// ``` - public func countInstructions(comp : () -> ()) : Nat64 { - let init = Prim.performanceCounter(0); - let pre = Prim.performanceCounter(0); - comp(); - let post = Prim.performanceCounter(0); - // performance_counter costs around 200 extra instructions; we perform an empty measurement to decide the overhead - let overhead = pre - init; - post - pre - overhead - }; - - /// Returns the current value of IC _performance counter_ `counter`. - /// - /// * Counter `0` is the _current execution instruction counter_, counting instructions only since the beginning of the current IC message. - /// This counter is reset to value `0` on shared function entry and every `await`. - /// It is therefore only suitable for measuring the cost of synchronous code. - /// - /// * Counter `1` is the _call context instruction counter_ for the current shared function call. - /// For replicated message executing, this excludes the cost of nested IC calls (even to the current canister). - /// For non-replicated messages, such as composite queries, it includes the cost of nested calls. - /// The current value of this counter is preserved across `awaits` (unlike counter `0`). - /// - /// * The function (currently) traps if `counter` >= 2. - /// - /// Consult [Performance Counter](https://internetcomputer.org/docs/current/references/ic-interface-spec#system-api-performance-counter) for details. - /// - /// Example: - /// ```motoko no-repl - /// import IC "mo:core/InternetComputer"; - /// - /// let c1 = IC.performanceCounter(1); - /// // ... - /// let diff : Nat64 = IC.performanceCounter(1) - c1; - /// ``` - public let performanceCounter : (counter : Nat32) -> (value : Nat64) = Prim.performanceCounter; - - /// Returns the time (in nanoseconds from the epoch start) by when the update message should - /// reply to the best effort message so that it can be received by the requesting canister. - /// Queries and unbounded-time update messages return null. - public func replyDeadline() : ?Nat { - let raw = Prim.replyDeadline(); - if (raw == 0) null else ?Prim.nat64ToNat(raw) - }; - -} diff --git a/.mops/core@2.3.1/src/Iter.mo b/.mops/core@2.3.1/src/Iter.mo deleted file mode 100644 index c78b99c..0000000 --- a/.mops/core@2.3.1/src/Iter.mo +++ /dev/null @@ -1,869 +0,0 @@ -/// Utilities for `Iter` (iterator) values. -/// -/// Iterators are a way to represent sequences of values that can be lazily produced. -/// They can be used to: -/// - Iterate over collections. -/// - Represent collections that are too large to fit in memory or that are produced incrementally. -/// - Transform collections without creating intermediate collections. -/// -/// Iterators are inherently stateful. Calling `next` "consumes" a value from -/// the Iterator that cannot be put back, so keep that in mind when sharing -/// iterators between consumers. -/// -/// ```motoko name=import -/// import Iter "mo:core/Iter"; -/// ``` -/// -/// -/// An iterator can be iterated over using a `for` loop: -/// ```motoko -/// let iter = [1, 2, 3].values(); -/// for (x in iter) { -/// // do something with x... -/// } -/// ``` -/// -/// Iterators can be: -/// - created from other collections (e.g. using `values` or `keys` function on a `Map`) or from scratch (e.g. using `empty` or `singleton`). -/// - transformed using `map`, `filter`, `concat`, etc. Which can be used to compose several transformations together without materializing intermediate collections. -/// - consumed using `forEach`, `size`, `toArray`, etc. -/// - combined using `concat`. - -import Prim "mo:prim"; - -import Array "Array"; -import Order "Order"; -import Runtime "Runtime"; -import Types "Types"; -import VarArray "VarArray"; - -module { - - /// An iterator that produces values of type `T`. Calling `next` returns - /// `null` when iteration is finished. - /// - /// Iterators are inherently stateful. Calling `next` "consumes" a value from - /// the Iterator that cannot be put back, so keep that in mind when sharing - /// iterators between consumers. - /// - /// An iterator `i` can be iterated over using - /// ```motoko - /// let iter = [1, 2, 3].values(); - /// for (x in iter) { - /// // do something with x... - /// } - /// ``` - public type Iter = Types.Iter; - - /// Creates an empty iterator. - /// - /// ```motoko include=import - /// for (x in Iter.empty()) - /// assert false; // This loop body will never run - /// ``` - public func empty() : Iter { - object { - public func next() : ?T { - null - } - } - }; - - /// Creates an iterator that produces a single value. - /// - /// ```motoko include=import - /// var sum = 0; - /// for (x in Iter.singleton(3)) - /// sum += x; - /// assert sum == 3; - /// ``` - public func singleton(value : T) : Iter { - object { - var state = ?value; - public func next() : ?T { - switch state { - case null null; - case some { - state := null; - some - } - } - } - } - }; - - /// Calls a function `f` on every value produced by an iterator and discards - /// the results. If you're looking to keep these results use `map` instead. - /// - /// ```motoko include=import - /// var sum = 0; - /// Iter.forEach([1, 2, 3].values(), func(x) { - /// sum += x; - /// }); - /// assert sum == 6; - /// ``` - public func forEach( - self : Iter, - f : (T) -> () - ) { - label l loop { - switch (self.next()) { - case (?next) { - f(next) - }; - case (null) { - break l - } - } - } - }; - - /// Takes an iterator and returns a new iterator that pairs each element with its index. - /// The index starts at 0 and increments by 1 for each element. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray(["A", "B", "C"]); - /// let enumerated = Iter.enumerate(iter); - /// let result = Iter.toArray(enumerated); - /// assert result == [(0, "A"), (1, "B"), (2, "C")]; - /// ``` - public func enumerate(self : Iter) : Iter<(Nat, T)> { - object { - var i = 0; - public func next() : ?(Nat, T) { - switch (self.next()) { - case (?x) { - let current = (i, x); - i += 1; - ?current - }; - case null { null } - } - } - } - }; - - /// Creates a new iterator that yields every nth element from the original iterator. - /// If `interval` is 0, returns an empty iterator. If `interval` is 1, returns the original iterator. - /// For any other positive interval, returns an iterator that skips `interval - 1` elements after each yielded element. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5, 6]); - /// let steppedIter = Iter.step(iter, 2); // Take every 2nd element - /// assert ?1 == steppedIter.next(); - /// assert ?3 == steppedIter.next(); - /// assert ?5 == steppedIter.next(); - /// assert null == steppedIter.next(); - /// ``` - public func step(self : Iter, n : Nat) : Iter { - if (n == 0) { - empty() - } else if (n == 1) { - self - } else { - object { - public func next() : ?T { - let item = self.next(); - var i = 1; - while (i < n) { - ignore self.next(); - i += 1 - }; - item - } - } - } - }; - - /// Consumes an iterator and counts how many elements were produced (discarding them in the process). - /// ```motoko include=import - /// let iter = [1, 2, 3].values(); - /// assert 3 == Iter.size(iter); - /// ``` - public func size(self : Iter) : Nat { - var len = 0; - forEach(self, func(x) { len += 1 }); - len - }; - - /// Takes a function and an iterator and returns a new iterator that lazily applies - /// the function to every element produced by the argument iterator. - /// ```motoko include=import - /// let iter = [1, 2, 3].values(); - /// let mappedIter = Iter.map(iter, func (x) = x * 2); - /// let result = Iter.toArray(mappedIter); - /// assert result == [2, 4, 6]; - /// ``` - public func map(self : Iter, f : T -> R) : Iter = object { - public func next() : ?R { - switch (self.next()) { - case (?next) { - ?f(next) - }; - case (null) { - null - } - } - } - }; - - /// Creates a new iterator that only includes elements from the original iterator - /// for which the predicate function returns true. - /// - /// ```motoko include=import - /// let iter = [1, 2, 3, 4, 5].values(); - /// let evenNumbers = Iter.filter(iter, func (x) = x % 2 == 0); - /// let result = Iter.toArray(evenNumbers); - /// assert result == [2, 4]; - /// ``` - public func filter(self : Iter, f : T -> Bool) : Iter = object { - public func next() : ?T { - loop { - let ?x = self.next() else return null; - if (f x) return ?x - }; - null - } - }; - - /// Creates a new iterator by applying a transformation function to each element - /// of the original iterator. Elements for which the function returns null are - /// excluded from the result. - /// - /// ```motoko include=import - /// let iter = [1, 2, 3].values(); - /// let evenNumbers = Iter.filterMap(iter, func (x) = if (x % 2 == 0) ?x else null); - /// let result = Iter.toArray(evenNumbers); - /// assert result == [2]; - /// ``` - public func filterMap(self : Iter, f : T -> ?R) : Iter = object { - public func next() : ?R { - loop { - let ?x = self.next() else return null; - switch (f x) { - case (?r) return ?r; - case null {} // continue - } - } - } - }; - - /// Flattens an iterator of iterators into a single iterator by concatenating the inner iterators. - /// - /// Possible optimization: Use `flatMap` when you need to transform elements before calling `flatten`. Example: use `flatMap(...)` instead of `flatten(map(...))`. - /// ```motoko include=import - /// let iter = Iter.flatten([[1, 2].values(), [3].values(), [4, 5, 6].values()].values()); - /// let result = Iter.toArray(iter); - /// assert result == [1, 2, 3, 4, 5, 6]; - /// ``` - public func flatten(self : Iter>) : Iter = object { - var current : Iter = empty(); - public func next() : ?T { - loop { - switch (current.next()) { - case (?x) return ?x; - case null { - let ?next = self.next() else return null; - current := next - } - } - } - } - }; - - /// Transforms every element of an iterator into an iterator and concatenates the results. - /// ```motoko include=import - /// let iter = Iter.flatMap([1, 3, 5].values(), func (x) = [x, x + 1].values()); - /// let result = Iter.toArray(iter); - /// assert result == [1, 2, 3, 4, 5, 6]; - /// ``` - public func flatMap(self : Iter, f : T -> Iter) : Iter = object { - var current : Iter = empty(); - public func next() : ?R { - loop { - switch (current.next()) { - case (?x) return ?x; - case null { - let ?next = self.next() else return null; - current := f(next) - } - } - } - } - }; - - /// Returns a new iterator that yields at most, first `n` elements from the original iterator. - /// After `n` elements have been produced or the original iterator is exhausted, - /// subsequent calls to `next()` will return `null`. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5]); - /// let first3 = Iter.take(iter, 3); - /// let result = Iter.toArray(first3); - /// assert result == [1, 2, 3]; - /// ``` - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3]); - /// let first5 = Iter.take(iter, 5); - /// let result = Iter.toArray(first5); - /// assert result == [1, 2, 3]; // only 3 elements in the original iterator - /// ``` - public func take(self : Iter, n : Nat) : Iter = object { - var remaining = n; - public func next() : ?T { - if (remaining == 0) return null; - remaining -= 1; - self.next() - } - }; - - /// Returns a new iterator that yields elements from the original iterator until the predicate function returns false. - /// The first element for which the predicate returns false is not included in the result. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5, 4, 3, 2, 1]); - /// let result = Iter.takeWhile(iter, func (x) = x < 4); - /// let array = Iter.toArray(result); - /// assert array == [1, 2, 3]; // note the difference between `takeWhile` and `filter` - /// ``` - public func takeWhile(self : Iter, f : T -> Bool) : Iter = object { - var done = false; - public func next() : ?T { - if done return null; - let ?x = self.next() else return null; - if (f x) return ?x; - done := true; - null - } - }; - - /// Returns a new iterator that skips the first `n` elements from the original iterator. - /// If the original iterator has fewer than `n` elements, the result will be an empty iterator. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5]); - /// let skipped = Iter.drop(iter, 3); - /// let result = Iter.toArray(skipped); - /// assert result == [4, 5]; - /// ``` - public func drop(self : Iter, n : Nat) : Iter = object { - var remaining = n; - public func next() : ?T { - while (remaining > 0) { - let ?_ = self.next() else return null; - remaining -= 1 - }; - self.next() - } - }; - - /// Returns a new iterator that skips elements from the original iterator until the predicate function returns false. - /// The first element for which the predicate returns false is the first element produced by the new iterator. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5, 4, 3, 2, 1]); - /// let result = Iter.dropWhile(iter, func (x) = x < 4); - /// let array = Iter.toArray(result); - /// assert array == [4, 5, 4, 3, 2, 1]; // notice that `takeWhile` and `dropWhile` are complementary - /// ``` - public func dropWhile(self : Iter, f : T -> Bool) : Iter = object { - var dropping = true; - public func next() : ?T { - while dropping { - let ?x = self.next() else return null; - if (not f x) { - dropping := false; - return ?x - } - }; - self.next() - } - }; - - /// Zips two iterators into a single iterator that produces pairs of elements. - /// The resulting iterator will stop producing elements when either of the input iterators is exhausted. - /// - /// ```motoko include=import - /// let iter1 = [1, 2, 3].values(); - /// let iter2 = ["A", "B"].values(); - /// let zipped = Iter.zip(iter1, iter2); - /// let result = Iter.toArray(zipped); - /// assert result == [(1, "A"), (2, "B")]; // note that the third element from iter1 is not included, because iter2 is exhausted - /// ``` - public func zip(self : Iter, other : Iter) : Iter<(A, B)> = object { - public func next() : ?(A, B) { - let ?x = self.next() else return null; - let ?y = other.next() else return null; - ?(x, y) - } - }; - - /// Zips three iterators into a single iterator that produces triples of elements. - /// The resulting iterator will stop producing elements when any of the input iterators is exhausted. - /// - /// ```motoko include=import - /// let iter1 = ["A", "B"].values(); - /// let iter2 = ["1", "2", "3"].values(); - /// let iter3 = ["x", "y", "z", "xd"].values(); - /// let zipped = Iter.zip3(iter1, iter2, iter3); - /// let result = Iter.toArray(zipped); - /// assert result == [("A", "1", "x"), ("B", "2", "y")]; // note that the unmatched elements from iter2 and iter3 are not included - /// ``` - public func zip3(self : Iter, other1 : Iter, other2 : Iter) : Iter<(A, B, C)> = object { - public func next() : ?(A, B, C) { - let ?x = self.next() else return null; - let ?y = other1.next() else return null; - let ?z = other2.next() else return null; - ?(x, y, z) - } - }; - - /// Zips two iterators into a single iterator by applying a function to zipped pairs of elements. - /// The resulting iterator will stop producing elements when either of the input iterators is exhausted. - /// - /// ```motoko include=import - /// let iter1 = ["A", "B"].values(); - /// let iter2 = ["1", "2", "3"].values(); - /// let zipped = Iter.zipWith(iter1, iter2, func (a, b) = a # b); - /// let result = Iter.toArray(zipped); - /// assert result == ["A1", "B2"]; // note that the third element from iter2 is not included, because iter1 is exhausted - /// ``` - public func zipWith(self : Iter, other : Iter, f : (A, B) -> R) : Iter = object { - public func next() : ?R { - let ?x = self.next() else return null; - let ?y = other.next() else return null; - ?f(x, y) - } - }; - - /// Zips three iterators into a single iterator by applying a function to zipped triples of elements. - /// The resulting iterator will stop producing elements when any of the input iterators is exhausted. - /// - /// ```motoko include=import - /// let iter1 = ["A", "B"].values(); - /// let iter2 = ["1", "2", "3"].values(); - /// let iter3 = ["x", "y", "z", "xd"].values(); - /// let zipped = Iter.zipWith3(iter1, iter2, iter3, func (a, b, c) = a # b # c); - /// let result = Iter.toArray(zipped); - /// assert result == ["A1x", "B2y"]; // note that the unmatched elements from iter2 and iter3 are not included - /// ``` - public func zipWith3(self : Iter, other1 : Iter, other2 : Iter, f : (A, B, C) -> R) : Iter = object { - public func next() : ?R { - let ?x = self.next() else return null; - let ?y = other1.next() else return null; - let ?z = other2.next() else return null; - ?f(x, y, z) - } - }; - - /// Checks if a predicate function is true for all elements produced by an iterator. - /// It stops consuming elements from the original iterator as soon as the predicate returns false. - /// - /// ```motoko include=import - /// assert Iter.all([1, 2, 3].values(), func (x) = x < 4); - /// assert not Iter.all([1, 2, 3].values(), func (x) = x < 3); - /// ``` - public func all(self : Iter, f : T -> Bool) : Bool { - for (x in self) { - if (not f x) return false - }; - true - }; - - /// Checks if a predicate function is true for any element produced by an iterator. - /// It stops consuming elements from the original iterator as soon as the predicate returns true. - /// - /// ```motoko include=import - /// assert Iter.any([1, 2, 3].values(), func (x) = x == 2); - /// assert not Iter.any([1, 2, 3].values(), func (x) = x == 4); - /// ``` - public func any(self : Iter, f : T -> Bool) : Bool { - for (x in self) { - if (f x) return true - }; - false - }; - - /// Finds the first element produced by an iterator for which a predicate function returns true. - /// Returns `null` if no such element is found. - /// It stops consuming elements from the original iterator as soon as the predicate returns true. - /// - /// ```motoko include=import - /// let iter = [1, 2, 3, 4].values(); - /// assert ?2 == Iter.find(iter, func (x) = x % 2 == 0); - /// ``` - public func find(self : Iter, f : T -> Bool) : ?T { - for (x in self) { - if (f x) return ?x - }; - null - }; - - /// Returns the first index in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let iter = ['A', 'B', 'C', 'D'].values(); - /// let found = Iter.findIndex(iter, func(x) { x == 'C' }); - /// assert found == ?2; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func findIndex(self : Iter, predicate : T -> Bool) : ?Nat { - for ((index, element) in enumerate(self)) { - if (predicate element) { - return ?index - } - }; - null - }; - - /// Checks if an element is produced by an iterator. - /// It stops consuming elements from the original iterator as soon as the predicate returns true. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3, 4].values(); - /// assert Iter.contains(iter, Nat.equal, 2); - /// ``` - public func contains(self : Iter, equal : (implicit : (T, T) -> Bool), value : T) : Bool { - for (x in self) { - if (equal(x, value)) return true - }; - false - }; - - /// Reduces an iterator to a single value by applying a function to each element and an accumulator. - /// The accumulator is initialized with the `initial` value. - /// It starts applying the `combine` function starting from the `initial` accumulator value and the first elements produced by the iterator. - /// - /// ```motoko include=import - /// let iter = ["A", "B", "C"].values(); - /// let result = Iter.foldLeft(iter, "S", func (acc, x) = "(" # acc # x # ")"); - /// assert result == "(((SA)B)C)"; - /// ``` - public func foldLeft(self : Iter, initial : R, combine : (R, T) -> R) : R { - var acc = initial; - for (x in self) { - acc := combine(acc, x) - }; - acc - }; - - /// Reduces an iterator to a single value by applying a function to each element in reverse order and an accumulator. - /// The accumulator is initialized with the `initial` value and it is first combined with the last element produced by the iterator. - /// It starts applying the `combine` function starting from the last elements produced by the iterator. - /// - /// **Performance note**: Since this function needs to consume the entire iterator to reverse it, - /// it has to materialize the entire iterator in memory to get to the last element to start applying the `combine` function. - /// **Use `foldLeft` or `reduce` when possible to avoid the extra memory overhead**. - /// - /// ```motoko include=import - /// let iter = ["A", "B", "C"].values(); - /// let result = Iter.foldRight(iter, "S", func (x, acc) = "(" # x # acc # ")"); - /// assert result == "(A(B(CS)))"; - /// ``` - public func foldRight(self : Iter, initial : R, combine : (T, R) -> R) : R { - foldLeft(reverse(self), initial, func(acc, x) = combine(x, acc)) - }; - - /// Reduces an iterator to a single value by applying a function to each element, starting with the first elements. - /// The accumulator is initialized with the first element produced by the iterator. - /// When the iterator is empty, it returns `null`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// assert ?6 == Iter.reduce(iter, Nat.add); - /// ``` - public func reduce(self : Iter, combine : (T, T) -> T) : ?T { - let ?first = self.next() else return null; - ?foldLeft(self, first, combine) - }; - - /// Produces an iterator containing cumulative results of applying the `combine` operator going left to right, including the `initial` value. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// let scanned = Iter.scanLeft(iter, 0, Nat.add); - /// let result = Iter.toArray(scanned); - /// assert result == [0, 1, 3, 6]; - /// ``` - public func scanLeft(self : Iter, initial : R, combine : (R, T) -> R) : Iter = object { - var acc = initial; - var isInitial = true; - public func next() : ?R { - if (isInitial) { - isInitial := false; - return ?acc - }; - switch (self.next()) { - case (?x) { - acc := combine(acc, x); - ?acc - }; - case null null - } - } - }; - - /// Produces an iterator containing cumulative results of applying the `combine` operator going right to left, including the `initial` value. - /// - /// **Performance note**: Since this function needs to consume the entire iterator to reverse it, - /// it has to materialize the entire iterator in memory to get to the last element to start applying the `combine` function. - /// **Use `scanLeft` when possible to avoid the extra memory overhead**. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// let scanned = Iter.scanRight(iter, 0, Nat.add); - /// let result = Iter.toArray(scanned); - /// assert result == [0, 3, 5, 6]; - /// ``` - public func scanRight(self : Iter, initial : R, combine : (T, R) -> R) : Iter { - scanLeft(reverse(self), initial, func(x, acc) = combine(acc, x)) - }; - - /// Creates an iterator that produces elements using the `step` function starting from the `initial` value. - /// The `step` function takes the current state and returns the next element and the next state, or `null` if the iteration is finished. - /// - /// ```motoko include=import - /// let iter = Iter.unfold(1, func (x) = if (x <= 3) ?(x, x + 1) else null); - /// let result = Iter.toArray(iter); - /// assert result == [1, 2, 3]; - /// ``` - public func unfold(initial : S, step : S -> ?(T, S)) : Iter = object { - var state = initial; - public func next() : ?T { - let ?(t, next) = step(state) else return null; - state := next; - ?t - } - }; - - // todo: unfold, iterate, cycle, range, rangeStep, rangeStepTo, rangeStepToExclusive - - /// Consumes an iterator and returns the first maximum element produced by the iterator. - /// If the iterator is empty, it returns `null`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// assert ?3 == Iter.max(iter, Nat.compare); - /// ``` - public func max(self : Iter, compare : (implicit : (T, T) -> Order.Order)) : ?T { - reduce( - self, - func(a, b) { - switch (compare(a, b)) { - case (#less) b; - case _ a - } - } - ) - }; - - /// Consumes an iterator and returns the first minimum element produced by the iterator. - /// If the iterator is empty, it returns `null`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// assert ?1 == Iter.min(iter, Nat.compare); - /// ``` - public func min(self : Iter, compare : (implicit : (T, T) -> Order.Order)) : ?T { - reduce( - self, - func(a, b) { - switch (compare(a, b)) { - case (#greater) b; - case _ a - } - } - ) - }; - - /// Creates an iterator that produces an infinite sequence of `x`. - /// ```motoko include=import - /// let iter = Iter.infinite(10); - /// assert ?10 == iter.next(); - /// assert ?10 == iter.next(); - /// assert ?10 == iter.next(); - /// // ... - /// ``` - public func infinite(item : T) : Iter = object { - public func next() : ?T { - ?item - } - }; - - /// Takes two iterators and returns a new iterator that produces - /// elements from the original iterators sequentally. - /// ```motoko include=import - /// let iter1 = [1, 2].values(); - /// let iter2 = [5, 6, 7].values(); - /// let concatenatedIter = Iter.concat(iter1, iter2); - /// let result = Iter.toArray(concatenatedIter); - /// assert result == [1, 2, 5, 6, 7]; - /// ``` - public func concat(self : Iter, other : Iter) : Iter { - var aEnded : Bool = false; - object { - public func next() : ?T { - if (aEnded) { - return other.next() - }; - switch (self.next()) { - case (?x) ?x; - case (null) { - aEnded := true; - other.next() - } - } - } - } - }; - - /// Creates an iterator that produces the elements of an Array in ascending index order. - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3]); - /// assert ?1 == iter.next(); - /// assert ?2 == iter.next(); - /// assert ?3 == iter.next(); - /// assert null == iter.next(); - /// ``` - /// @deprecated M0235 - public func fromArray(array : [T]) : Iter = array.vals(); - - /// Like `fromArray` but for Arrays with mutable elements. Captures - /// the elements of the Array at the time the iterator is created, so - /// further modifications won't be reflected in the iterator. - /// @deprecated M0235 - public func fromVarArray(array : [var T]) : Iter = array.vals(); - - /// Consumes an iterator and collects its produced elements in an Array. - /// ```motoko include=import - /// let iter = [1, 2, 3].values(); - /// assert [1, 2, 3] == Iter.toArray(iter); - /// ``` - public func toArray(self : Iter) : [T] { - // TODO: Replace implementation. This is just temporay. - type Node = { value : T; var next : ?Node }; - var first : ?Node = null; - var last : ?Node = null; - var count = 0; - - func add(value : T) { - let node : Node = { value; var next = null }; - switch (last) { - case null { - first := ?node - }; - case (?previous) { - previous.next := ?node - } - }; - last := ?node; - count += 1 - }; - - for (value in self) { - add(value) - }; - if (count == 0) { - return [] - }; - var current = first; - Prim.Array_tabulate( - count, - func(_) { - switch (current) { - case null Runtime.trap("Iter.toArray(): node must not be null"); - case (?node) { - current := node.next; - node.value - } - } - } - ) - }; - - /// Like `toArray` but for Arrays with mutable elements. - public func toVarArray(self : Iter) : [var T] { - Array.toVarArray(toArray(self)) - }; - - /// Sorted iterator. Will iterate over *all* elements to sort them, necessarily. - public func sort(self : Iter, compare : (implicit : (T, T) -> Order.Order)) : Iter { - let array = toVarArray(self); - VarArray.sortInPlace(array, compare); - fromVarArray(array) - }; - - /// Creates an iterator that produces a given item a specified number of times. - /// ```motoko include=import - /// let iter = Iter.repeat(3, 2); - /// assert ?3 == iter.next(); - /// assert ?3 == iter.next(); - /// assert null == iter.next(); - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func repeat(item : T, count : Nat) : Iter = object { - var remaining = count; - public func next() : ?T { - if (remaining == 0) { - null - } else { - remaining -= 1; - ?item - } - } - }; - - /// Creates a new iterator that produces elements from the original iterator in reverse order. - /// Note: This function needs to consume the entire iterator to reverse it. - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3]); - /// let reversed = Iter.reverse(iter); - /// assert ?3 == reversed.next(); - /// assert ?2 == reversed.next(); - /// assert ?1 == reversed.next(); - /// assert null == reversed.next(); - /// ``` - /// - /// Runtime: O(n) where n is the number of elements in the iterator - /// - /// Space: O(n) where n is the number of elements in the iterator - public func reverse(self : Iter) : Iter { - var acc : Types.Pure.List = null; - for (x in self) { - acc := ?(x, acc) - }; - object { - public func next() : ?T { - switch acc { - case null null; - case (?(h, t)) { - acc := t; - ?h - } - } - } - } - }; - -} diff --git a/.mops/core@2.3.1/src/List.mo b/.mops/core@2.3.1/src/List.mo deleted file mode 100644 index 07f98eb..0000000 --- a/.mops/core@2.3.1/src/List.mo +++ /dev/null @@ -1,3138 +0,0 @@ -/// A mutable growable array data structure with efficient random access and dynamic resizing. -/// `List` provides O(1) access time and O(sqrt(n)) memory overhead. In contrast, `pure/List` is a purely functional linked list. -/// Can be declared `stable` for orthogonal persistence. -/// -/// This implementation is adapted with permission from the `vector` Mops package created by Research AG. -/// -/// Copyright: 2023 MR Research AG -/// Main author: Andrii Stepanov (AStepanov25) -/// Contributors: Timo Hanke (timohanke), Andy Gura (andygura), react0r-com -/// -/// ```motoko name=import -/// import List "mo:core/List"; -/// ``` - -import PureList "pure/List"; -import Prim "mo:⛔"; -import Nat32 "Nat32"; -import Array "Array"; -import Nat "Nat"; -import Option "Option"; -import VarArray "VarArray"; -import Types "Types"; - -module { - /// `List` provides a mutable list of elements of type `T`. - /// Based on the paper "Resizable Arrays in Optimal Time and Space" by Brodnik, Carlsson, Demaine, Munro and Sedgewick (1999). - /// Since this is internally a two-dimensional array the access times for put and get operations - /// will naturally be 2x slower than Buffer and Array. However, Array is not resizable and Buffer - /// has `O(size)` memory waste. - /// - /// The maximum number of elements in a `List` is 2^32. - public type List = Types.List; - - let INTERNAL_ERROR = "List: internal error"; - - /// Creates a new empty List for elements of type T. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); // Creates a new List - /// ``` - public func empty() : List = { - // the first block is always empty and is present in each List - // this is done to optimize locate, at, get, etc - var blocks = [var [var]]; - // can't be 0 in any List - var blockIndex = 1; - var elementIndex = 0 - }; - - /// Returns a new list with capacity and size 1, containing `element`. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.singleton(1); - /// assert List.toText(list, Nat.toText) == "List[1]"; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func singleton(element : T) : List = { - var blockIndex = 2; - var blocks = [var [var], [var ?element]]; - var elementIndex = 0 - }; - - func repeatInternal(initValue : ?T, size : Nat) : List { - let (blockIndex, elementIndex) = locate(size); - - let blocks = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) { blockIndex - 1 } else blockIndex)); - let dataBlocks = VarArray.repeat<[var ?T]>([var], blocks); - var i = 1; - while (i < blockIndex) { - dataBlocks[i] := VarArray.repeat(initValue, dataBlockSize(i)); - i += 1 - }; - if (elementIndex != 0) { - dataBlocks[blockIndex] := if (Option.isNull(initValue)) VarArray.repeat( - null, - dataBlockSize(blockIndex) - ) else VarArray.tabulate( - dataBlockSize(blockIndex), - func i = if (i < elementIndex) initValue else null - ) - }; - - { - var blocks = dataBlocks; - var blockIndex = blockIndex; - var elementIndex = elementIndex - } - }; - - /// Creates a new List with `size` copies of the initial value. - /// - /// Example: - /// ```motoko include=import - /// let list = List.repeat(2, 4); - /// assert List.toArray(list) == [2, 2, 2, 2]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func repeat(initValue : T, size : Nat) : List = repeatInternal(?initValue, size); - - /// Fills all elements in the list with the given value. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3]); - /// List.fill(list, 0); // fills the list with 0 - /// assert List.toArray(list) == [0, 0, 0]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func fill(self : List, value : T) { - let blocks = self.blocks; - let blockCount = blocks.size(); - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = if (i == blockIndex) elementIndex else db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - db[j] := ?value; - j += 1 - }; - i += 1 - } - }; - - /// Converts a mutable `List` to a purely functional `PureList`. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3]); - /// let pureList = List.toPure(list); // converts to immutable PureList - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// @deprecated M0235 - public func toPure(self : List) : PureList.List { - var result : PureList.List = null; - - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) result := ?(x, result); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - }; - - result - }; - - /// Converts a purely functional `PureList` to a `List`. - /// - /// Example: - /// ```motoko include=import - /// import PureList "mo:core/pure/List"; - /// - /// let pureList = PureList.fromArray([1, 2, 3]); - /// let list = List.fromPure(pureList); // converts to List - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// @deprecated M0235 - public func fromPure(pure : PureList.List) : List { - var p = pure; - var list = empty(); - loop { - switch (p) { - case (?(x, xs)) { - add(list, x); - p := xs - }; - case null return list - } - } - }; - - func addRepeatInternal(list : List, initValue : ?T, count : Nat) { - let (b, e) = locate(size(list) + count); - let blocksCount = newIndexBlockLength(Nat32.fromNat(if (e == 0) b - 1 else b)); - - let oldBlocksCount = list.blocks.size(); - if (oldBlocksCount < blocksCount) { - let oldBlocks = list.blocks; - let blocks = VarArray.repeat<[var ?T]>([var], blocksCount); - var i = 0; - while (i < oldBlocksCount) { - blocks[i] := oldBlocks[i]; - i += 1 - }; - list.blocks := blocks - }; - - let blocks = list.blocks; - var blockIndex = list.blockIndex; - var elementIndex = list.elementIndex; - - var cnt = count; - label L while (cnt > 0) { - if (blocks[blockIndex].size() == 0) { - let dbSize = dataBlockSize(blockIndex); - if (cnt >= dbSize) { - blocks[blockIndex] := VarArray.repeat(initValue, dbSize); - blockIndex += 1; - cnt -= dbSize; - continue L - }; - blocks[blockIndex] := VarArray.repeat(null, dbSize) - }; - - let block = blocks[blockIndex]; - let dbSize = block.size(); - let to = Nat.min(elementIndex + cnt, dbSize); - cnt -= to - elementIndex; - - while (elementIndex < to) { - block[elementIndex] := initValue; - elementIndex += 1 - }; - - if (elementIndex == dbSize) { - elementIndex := 0; - blockIndex += 1 - } - }; - - list.blockIndex := blockIndex; - list.elementIndex := elementIndex - }; - - private func reserve(list : List, size : Nat) { - let blockIndex = list.blockIndex; - let elementIndex = list.elementIndex; - - addRepeatInternal(list, null, size); - - list.blockIndex := blockIndex; - list.elementIndex := elementIndex - }; - - /// Add to list `count` copies of the initial value. - /// - /// ```motoko include=import - /// let list = List.repeat(2, 4); // [2, 2, 2, 2] - /// List.addRepeat(list, 2, 1); // [2, 2, 2, 2, 1, 1] - /// ``` - /// - /// The maximum number of elements in a `List` is 2^32. - /// - /// Runtime: `O(count)` - public func addRepeat(self : List, initValue : T, count : Nat) = addRepeatInternal(self, ?initValue, count); - - /// Truncates the list to the specified size. - /// If the new size is larger than the current size, it will do nothing. - /// If the new size is equal to the current list size, after the operation list will be equal to cloned version of itself. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4, 5]); - /// List.truncate(list, 3); // list is now [1, 2, 3] - /// assert List.toArray(list) == [1, 2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func truncate(self : List, newSize : Nat) { - if (newSize > size(self)) return; - - // if newSize == size(self) then after the operation self will be equal to List.clone(self) - let (blockIndex, elementIndex) = locate(newSize); - self.blockIndex := blockIndex; - self.elementIndex := elementIndex; - let newBlocksCount = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) blockIndex - 1 else blockIndex)); - - let newBlocks = if (newBlocksCount < self.blocks.size()) { - let oldDataBlocks = self.blocks; - self.blocks := VarArray.tabulate<[var ?T]>(newBlocksCount, func(i) = oldDataBlocks[i]); - self.blocks - } else self.blocks; - - var i = if (elementIndex == 0) blockIndex else blockIndex + 1; - while (i < newBlocksCount) { - newBlocks[i] := [var]; - i += 1 - }; - if (elementIndex != 0) { - let block = newBlocks[blockIndex]; - var i = elementIndex; - var to = block.size(); - while (i < to) { - block[i] := null; - i += 1 - } - } - }; - - /// Resets the list to size 0, de-referencing all elements. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// List.clear(list); // list is now empty - /// assert List.toArray(list) == []; - /// ``` - /// - /// Runtime: `O(1)` - public func clear(self : List) { - self.blocks := [var [var]]; - self.blockIndex := 1; - self.elementIndex := 0 - }; - - /// Creates a list of size `size`. Each element at index i - /// is created by applying `generator` to i. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.tabulate(4, func i = i * 2); - /// assert List.toArray(list) == [0, 2, 4, 6]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `generator` runs in O(1) time and space. - public func tabulate(size : Nat, generator : Nat -> T) : List { - let (blockIndex, elementIndex) = locate(size); - - let blocks = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) { blockIndex - 1 } else blockIndex)); - let dataBlocks = VarArray.repeat<[var ?T]>([var], blocks); - - var i = 1; - var pos = 0; - - while (i < blockIndex) { - let len = dataBlockSize(i); - dataBlocks[i] := VarArray.tabulate(len, func i = ?generator(pos + i)); - pos += len; - i += 1 - }; - if (elementIndex != 0 and blockIndex < blocks) { - dataBlocks[i] := VarArray.tabulate( - dataBlockSize(blockIndex), - func i = if (i < elementIndex) ?generator(pos + i) else null - ) - }; - - { - var blocks = dataBlocks; - var blockIndex = blockIndex; - var elementIndex = elementIndex - } - }; - - /// Combines a list of lists into a single list. Retains the original - /// ordering of the elements. - /// - /// This has better performance compared to `List.join()`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let lists = List.fromArray>([ - /// List.fromArray([0, 1, 2]), List.fromArray([2, 3]), List.fromArray([]), List.fromArray([4]) - /// ]); - /// let flatList = List.flatten(lists); - /// assert List.equal(flatList, List.fromArray([0, 1, 2, 2, 3, 4]), Nat.equal); - /// ``` - /// - /// Runtime: O(number of elements in list) - /// - /// Space: O(number of elements in list) - public func flatten(self : List>) : List { - var sz = 0; - forEach>(self, func(sublist) = sz += size(sublist)); - - let result = repeatInternal(null, sz); - result.blockIndex := 1; - result.elementIndex := 0; - - forEach>( - self, - func(sublist) { - forEach( - sublist, - func(item) { - add(result, item) - } - ) - } - ); - result - }; - - /// Combines an iterator of lists into a single list. - /// Retains the original ordering of the elements. - /// - /// Consider using `List.flatten()` for better performance. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let lists = [List.fromArray([0, 1, 2]), List.fromArray([2, 3]), List.fromArray([]), List.fromArray([4])]; - /// let joinedList = List.join(lists.vals()); - /// assert List.equal(joinedList, List.fromArray([0, 1, 2, 2, 3, 4]), Nat.equal); - /// ``` - /// - /// Runtime: O(number of elements in list) - /// - /// Space: O(number of elements in list) - public func join(self : Types.Iter>) : List { - var result = empty(); - for (list in self) { - reserve(result, size(list)); - forEach(list, func item = addUnsafe(result, item)) - }; - result - }; - - /// Returns a copy of a List, with the same size. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 1); - /// - /// let clone = List.clone(list); - /// assert List.toArray(clone) == [1]; - /// ``` - /// - /// Runtime: `O(size)` - public func clone(self : List) : List = { - var blocks = VarArray.tabulate<[var ?T]>( - Nat.min( - newIndexBlockLength(Nat32.fromNat(if (self.elementIndex == 0) self.blockIndex - 1 else self.blockIndex)), - self.blocks.size() - ), - func(i) = VarArray.clone(self.blocks[i]) - ); - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - }; - - /// Creates a new list by applying the provided function to each element in the input list. - /// The resulting list has the same size as the input list. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.singleton(123); - /// let textList = List.map(list, Nat.toText); - /// assert List.toArray(textList) == ["123"]; - /// ``` - /// - /// Runtime: `O(size)` - public func map(self : List, f : T -> R) : List { - let blocksCount = Nat.min( - newIndexBlockLength(Nat32.fromNat(if (self.elementIndex == 0) self.blockIndex - 1 else self.blockIndex)), - self.blocks.size() - ); - let blocks = VarArray.repeat<[var ?R]>([var], blocksCount); - - var i = 1; - label l while (i < blocksCount) { - let oldBlock = self.blocks[i]; - let blockSize = oldBlock.size(); - let newBlock = VarArray.repeat(null, blockSize); - blocks[i] := newBlock; - var j = 0; - - while (j < blockSize) { - switch (oldBlock[j]) { - case (?item) newBlock[j] := ?f(item); - case null break l - }; - j += 1 - }; - i += 1 - }; - - { - var blocks = blocks; - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - } - }; - - /// Applies `f` to each element of `list` in place, - /// retaining the original ordering of elements. - /// This modifies the original list. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([0, 1, 2, 3]); - /// List.mapInPlace(list, func x = x * 3); - /// assert List.equal(list, List.fromArray([0, 3, 6, 9]), Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapInPlace(self : List, f : T -> T) { - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) db[j] := ?f(x); - case null return - }; - j += 1 - }; - i += 1 - } - }; - - /// Creates a new list by applying `f` to each element in `list` and its index. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([10, 10, 10, 10]); - /// let newList = List.mapEntries(list, func (x, i) = i * x); - /// assert List.equal(newList, List.fromArray([0, 10, 20, 30]), Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapEntries(self : List, f : (T, Nat) -> R) : List { - let blocks = VarArray.repeat<[var ?R]>([var], self.blocks.size()); - let blocksCount = self.blocks.size(); - - var index = 0; - - var i = 1; - label l while (i < blocksCount) { - let oldBlock = self.blocks[i]; - let blockSize = oldBlock.size(); - let newBlock = VarArray.repeat(null, blockSize); - blocks[i] := newBlock; - var j = 0; - - while (j < blockSize) { - switch (oldBlock[j]) { - case (?item) newBlock[j] := ?f(item, index); - case null break l - }; - j += 1; - index += 1 - }; - i += 1 - }; - - { - var blocks = blocks; - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - } - }; - - /// Creates a new list by applying `f` to each element in `list`. - /// If any invocation of `f` produces an `#err`, returns an `#err`. Otherwise - /// returns an `#ok` containing the new list. - /// - /// ```motoko include=import - /// import Result "mo:core/Result"; - /// - /// let list = List.fromArray([4, 3, 2, 1, 0]); - /// // divide 100 by every element in the list - /// let result = List.mapResult(list, func x { - /// if (x > 0) { - /// #ok(100 / x) - /// } else { - /// #err "Cannot divide by zero" - /// } - /// }); - /// assert Result.isErr(result); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapResult(self : List, f : T -> Types.Result) : Types.Result, E> { - var error : ?E = null; - - let blocks = VarArray.repeat<[var ?R]>([var], self.blocks.size()); - let blocksCount = self.blocks.size(); - - var i = 1; - while (i < blocksCount) { - let oldBlock = self.blocks[i]; - let blockSize = oldBlock.size(); - let newBlock = VarArray.repeat(null, blockSize); - blocks[i] := newBlock; - var j = 0; - - while (j < blockSize) { - switch (oldBlock[j]) { - case (?item) newBlock[j] := switch (f(item)) { - case (#ok x) ?x; - case (#err e) switch (error) { - case (null) { - error := ?e; - null - }; - case (?_) null - } - }; - case null return switch (error) { - case (null) return #ok { - var blocks = blocks; - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - }; - case (?e) return #err e - } - }; - j += 1 - }; - i += 1 - }; - - switch (error) { - case (null) return #ok { - var blocks = blocks; - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - }; - case (?e) return #err e - } - }; - - /// Returns a new list containing only the elements from `list` for which the predicate returns true. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4]); - /// let evenNumbers = List.filter(list, func x = x % 2 == 0); - /// assert List.toArray(evenNumbers) == [2, 4]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func filter(self : List, predicate : T -> Bool) : List { - let filtered = empty(); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return filtered; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (predicate(x)) add(filtered, x); - case null return filtered - }; - j += 1 - }; - i += 1 - }; - - filtered - }; - - /// Retains only the elements in `list` for which the predicate returns true. - /// Modifies the original list in place. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4]); - /// List.retain(list, func x = x % 2 == 0); - /// assert List.toArray(list) == [2, 4]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(sqrt(size))` if `list` was truncated otherwise `O(1)` - public func retain(self : List, predicate : T -> Bool) { - self.blockIndex := 1; - self.elementIndex := 0; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - label l while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) break l; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (predicate(x)) addUnsafe(self, x); - case null break l - }; - j += 1 - }; - i += 1 - }; - - truncate(self, size(self)) - }; - - /// Returns a new list containing all elements from `list` for which the function returns ?element. - /// Discards all elements for which the function returns null. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4]); - /// let doubled = List.filterMap(list, func x = if (x % 2 == 0) ?(x * 2) else null); - /// assert List.toArray(doubled) == [4, 8]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func filterMap(self : List, f : T -> ?R) : List { - let filtered = empty(); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return filtered; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) switch (f(x)) { - case (?y) add(filtered, y); - case null {} - }; - case null return filtered - }; - j += 1 - }; - i += 1 - }; - - filtered - }; - - /// Creates a new list by applying `k` to each element in `list`, - /// and concatenating the resulting iterators in order. - /// - /// ```motoko include=import - /// import Int "mo:core/Int" - /// - /// let list = List.fromArray([1, 2, 3, 4]); - /// let newList = List.flatMap(list, func x = [x, -x].vals()); - /// assert List.equal(newList, List.fromArray([1, -1, 2, -2, 3, -3, 4, -4]), Int.equal); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `k` runs in O(1) time and space. - public func flatMap(self : List, k : T -> Types.Iter) : List { - let result = empty(); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return result; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) for (y in k(x)) add(result, y); - case _ return result - }; - j += 1 - }; - i += 1 - }; - - result - }; - - func indexByBlockElement(blockIndex : Nat, elementIndex : Nat) : Nat { - let d = Nat32.fromNat(blockIndex); - - // We call all data blocks of the same capacity an "epoch". We number the epochs 0,1,2,... - // A data block is in epoch e iff the data block has capacity 2 ** e. - // Each epoch starting with epoch 1 spans exactly two super blocks. - // Super block s falls in epoch ceil(s/2). - - // epoch of last data block - // e = 32 - lz - let lz = Nat32.bitcountLeadingZero(d / 3); - - // capacity of all prior epochs combined - // capacity_before_e = 2 * 4 ** (e - 1) - 1 - - // data blocks in all prior epochs combined - // blocks_before_e = 3 * 2 ** (e - 1) - 2 - - // then size = d * 2 ** e + i - c - // where c = blocks_before_e * 2 ** e - capacity_before_e - - // there can be overflows, but the result is without overflows, so use addWrap and subWrap - // we don't erase bits by >>, so to use <>> is ok - Nat32.toNat((d -% (1 <>> lz)) <>> lz +% Nat32.fromNat(elementIndex)) - }; - - /// Returns the current number of elements in the list. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// assert List.size(list) == 0 - /// ``` - /// - /// Runtime: `O(1)` (with some internal calculations) - public func size(self : List) : Nat { - // due to the design of List (blockIndex, elementIndex) pair points - // exactly to the place where size-th element should be added - // so, it's the inlined version of indexByBlockElement - let d = Nat32.fromNat(self.blockIndex); - let lz = Nat32.bitcountLeadingZero(d / 3); - Nat32.toNat((d -% (1 <>> lz)) <>> lz +% Nat32.fromNat(self.elementIndex)) - }; - - func dataBlockSize(blockIndex : Nat) : Nat { - // formula for the size of given blockIndex - // don't call it for blockIndex == 0 - Nat32.toNat(1 <>> Nat32.bitcountLeadingZero(Nat32.fromNat(blockIndex) / 3)) - }; - - func newIndexBlockLength(blockIndex : Nat32) : Nat { - if (blockIndex <= 1) 2 else { - let s = 30 - Nat32.bitcountLeadingZero(blockIndex); - Nat32.toNat(((blockIndex >> s) +% 1) << s) - } - }; - - func growIndexBlockIfNeeded(list : List) { - if (list.blocks.size() == list.blockIndex) { - let newBlocks = VarArray.repeat<[var ?T]>([var], newIndexBlockLength(Nat32.fromNat(list.blockIndex))); - var i = 0; - while (i < list.blockIndex) { - newBlocks[i] := list.blocks[i]; - i += 1 - }; - list.blocks := newBlocks - } - }; - - func shrinkIndexBlockIfNeeded(list : List) { - let blockIndex = Nat32.fromNat(list.blockIndex); - // kind of index of the first block in the super block - if ((blockIndex << Nat32.bitcountLeadingZero(blockIndex)) << 2 == 0) { - let newLength = newIndexBlockLength(blockIndex); - if (newLength < list.blocks.size()) { - let newBlocks = VarArray.repeat<[var ?T]>([var], newLength); - var i = 0; - while (i < newLength) { - newBlocks[i] := list.blocks[i]; - i += 1 - }; - list.blocks := newBlocks - } - } - }; - - /// Adds a single element to the end of a List, - /// allocating a new internal data block if needed, - /// and resizing the internal index block if needed. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 0); // add 0 to list - /// List.add(list, 1); - /// List.add(list, 2); - /// List.add(list, 3); - /// assert List.toArray(list) == [0, 1, 2, 3]; - /// ``` - /// - /// The maximum number of elements in a `List` is 2^32. - /// - /// Amortized Runtime: `O(1)`, Worst Case Runtime: `O(sqrt(n))` - public func add(self : List, element : T) { - var elementIndex = self.elementIndex; - if (elementIndex == 0) { - growIndexBlockIfNeeded(self); - let blockIndex = self.blockIndex; - - // When removing last we keep one more data block, so can be not empty - if (self.blocks[blockIndex].size() == 0) { - self.blocks[blockIndex] := VarArray.repeat( - null, - dataBlockSize(blockIndex) - ) - } - }; - - let lastDataBlock = self.blocks[self.blockIndex]; - - lastDataBlock[elementIndex] := ?element; - - elementIndex += 1; - if (elementIndex == lastDataBlock.size()) { - elementIndex := 0; - self.blockIndex += 1 - }; - self.elementIndex := elementIndex - }; - - // Add an element without checking and resizing the List - private func addUnsafe(list : List, element : T) { - var elementIndex = list.elementIndex; - let lastDataBlock = list.blocks[list.blockIndex]; - lastDataBlock[elementIndex] := ?element; - - elementIndex += 1; - if (elementIndex == lastDataBlock.size()) { - elementIndex := 0; - list.blockIndex += 1 - }; - list.elementIndex := elementIndex - }; - - /// Removes and returns the last item in the list or `null` if - /// the list is empty. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// assert List.removeLast(list) == ?11; - /// assert List.removeLast(list) == ?10; - /// assert List.removeLast(list) == null; - /// ``` - /// - /// Amortized Runtime: `O(1)`, Worst Case Runtime: `O(sqrt(n))` - /// - /// Amortized Space: `O(1)`, Worst Case Space: `O(sqrt(n))` - public func removeLast(self : List) : ?T { - var elementIndex = self.elementIndex; - if (elementIndex == 0) { - var blockIndex = self.blockIndex; - if (blockIndex == 1) { - return null - }; - - shrinkIndexBlockIfNeeded(self); - - blockIndex -= 1; - elementIndex := self.blocks[blockIndex].size(); - - // Keep one totally empty block when removing - if (blockIndex + 2 < self.blocks.size()) self.blocks[blockIndex + 2] := [var]; - - self.blockIndex := blockIndex - }; - elementIndex -= 1; - - let lastDataBlock = self.blocks[self.blockIndex]; - - let element = lastDataBlock[elementIndex]; - lastDataBlock[elementIndex] := null; - - self.elementIndex := elementIndex; - return element - }; - - func locate(index : Nat) : (Nat, Nat) { - // see comments in tests - let i = Nat32.fromNat(index); - let lz = Nat32.bitcountLeadingZero(i); - let lz2 = lz >> 1; - if (lz & 1 == 0) { - (Nat32.toNat(((i << lz2) >> 16) ^ (0x10000 >> lz2)), Nat32.toNat(i & (0xFFFF >> lz2))) - } else { - (Nat32.toNat(((i << lz2) >> 15) ^ (0x18000 >> lz2)), Nat32.toNat(i & (0x7FFF >> lz2))) - } - }; - - /// Returns the element at index `index`. Indexing is zero-based. - /// Traps if `index >= size`, error message may not be descriptive. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// assert List.at(list, 0) == 10; - /// ``` - /// - /// Runtime: `O(1)` - public func at(self : List, index : Nat) : T { - // inlined version of: - // let (a,b) = locate(index); - // switch(self.blocks[a][b]) { - // case (?element) element; - // case (null) Prim.trap ""; - // }; - let i = Nat32.fromNat(index); - let lz = Nat32.bitcountLeadingZero(i); - let lz2 = lz >> 1; - switch ( - if (lz & 1 == 0) { - self.blocks[Nat32.toNat(((i << lz2) >> 16) ^ (0x10000 >> lz2))][Nat32.toNat(i & (0xFFFF >> lz2))] - } else { - self.blocks[Nat32.toNat(((i << lz2) >> 15) ^ (0x18000 >> lz2))][Nat32.toNat(i & (0x7FFF >> lz2))] - } - ) { - case (?result) return result; - case (_) Prim.trap "List index out of bounds in get" - } - }; - - /// Returns the element at index `index` as an option. - /// Returns `null` when `index >= size`. Indexing is zero-based. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// assert List.get(list, 0) == ?10; - /// assert List.get(list, 2) == null; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - /// @deprecated M0235 - public func get(self : List, index : Nat) : ?T { - // inlined version of locate - let (a, b) = do { - let i = Nat32.fromNat(index); - let lz = Nat32.bitcountLeadingZero(i); - let lz2 = lz >> 1; - if (lz & 1 == 0) { - (Nat32.toNat(((i << lz2) >> 16) ^ (0x10000 >> lz2)), Nat32.toNat(i & (0xFFFF >> lz2))) - } else { - (Nat32.toNat(((i << lz2) >> 15) ^ (0x18000 >> lz2)), Nat32.toNat(i & (0x7FFF >> lz2))) - } - }; - if (a < self.blockIndex or self.elementIndex != 0 and a == self.blockIndex) { - self.blocks[a][b] - } else null - }; - - /// Overwrites the current element at `index` with `element`. - /// Traps if `index` >= size, error message may not be descriptive. Indexing is zero-based. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.put(list, 0, 20); // overwrites 10 at index 0 with 20 - /// assert List.toArray(list) == [20]; - /// ``` - /// - /// Runtime: `O(1)` - public func put(self : List, index : Nat, value : T) { - let i = Nat32.fromNat(index); - let lz = Nat32.bitcountLeadingZero(i); - let lz2 = lz >> 1; - let (block, element) = if (lz & 1 == 0) { - (self.blocks[Nat32.toNat(((i << lz2) >> 16) ^ (0x10000 >> lz2))], Nat32.toNat(i & (0xFFFF >> lz2))) - } else { - (self.blocks[Nat32.toNat(((i << lz2) >> 15) ^ (0x18000 >> lz2))], Nat32.toNat(i & (0x7FFF >> lz2))) - }; - - switch (block[element]) { - case (?_) block[element] := ?value; - case _ Prim.trap "List index out of bounds in put" - } - }; - - /// Sorts the elements in the list according to `compare`. - /// Sort is deterministic, stable, and in-place. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 3); - /// List.add(list, 1); - /// List.add(list, 2); - /// List.sortInPlace(list, Nat.compare); - /// assert List.toArray(list) == [1, 2, 3]; - /// ``` - /// - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sortInPlace(self : List, compare : (implicit : (T, T) -> Types.Order)) { - if (size(self) < 2) return; - let array = toVarArray(self); - - VarArray.sortInPlace(array, compare); - - var index = 0; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?_) db[j] := ?array[index]; - case _ return - }; - index += 1; - j += 1 - }; - i += 1 - } - }; - - /// Sorts the elements in the list according to `compare`. - /// Sort is deterministic, stable, and in-place. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 3); - /// List.add(list, 1); - /// List.add(list, 2); - /// let sorted = List.sort(list, Nat.compare); - /// assert List.toArray(sorted) == [1, 2, 3]; - /// ``` - /// - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sort(self : List, compare : (implicit : (T, T) -> Types.Order)) : List { - let array = toVarArray(self); - VarArray.sortInPlace(array, compare); - fromVarArray(array) - }; - - /// Checks whether the `list` is sorted. - /// - /// Example: - /// ``` - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// assert List.isSorted(list, Nat.compare); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func isSorted(self : List, compare : (implicit : (T, T) -> Types.Order)) : Bool { - var prev = switch (first(self)) { - case (?x) x; - case _ return true - }; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return true; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) switch (compare(x, prev)) { - case (#greater or #equal) prev := x; - case (#less) return false - }; - case null return true - }; - j += 1 - }; - i += 1 - }; - - true - }; - - /// Remove adjacent duplicates from the `list`, if the `list` is sorted all elements will be unique. - /// - /// Example: - /// ``` - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 1, 2, 2, 3]); - /// List.deduplicate(list, Nat.equal); - /// assert List.equal(list, List.fromArray([1, 2, 3]), Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func deduplicate(self : List, equal : (implicit : (T, T) -> Bool)) { - var prev = switch (first(self)) { - case (?x) x; - case _ return - }; - - self.blockIndex := 1; - self.elementIndex := 0; - - addUnsafe(self, prev); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - label l while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return break l; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) { - if (not equal(x, prev)) addUnsafe(self, x); - prev := x - }; - case null break l - }; - j += 1 - }; - i += 1 - }; - - truncate(self, size(self)) - }; - - /// Finds the first index of `element` in `list` using equality of elements defined - /// by `equal`. Returns `null` if `element` is not found. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.indexOf(list, Nat.equal, 3) == ?2; - /// assert List.indexOf(list, Nat.equal, 5) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// *Runtime and space assumes that `equal` runs in `O(1)` time and space. - public func indexOf(self : List, equal : (implicit : (T, T) -> Bool), element : T) : ?Nat { - if (isEmpty(self)) return null; - nextIndexOf(self, equal, element, 0) - }; - - /// Returns the index of the next occurence of `element` in the `list` starting from the `from` index (inclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let list = List.fromArray(['c', 'o', 'f', 'f', 'e', 'e']); - /// assert List.nextIndexOf(list, Char.equal, 'c', 0) == ?0; - /// assert List.nextIndexOf(list, Char.equal, 'f', 0) == ?2; - /// assert List.nextIndexOf(list, Char.equal, 'f', 2) == ?2; - /// assert List.nextIndexOf(list, Char.equal, 'f', 3) == ?3; - /// assert List.nextIndexOf(list, Char.equal, 'f', 4) == null; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func nextIndexOf(self : List, equal : (implicit : (T, T) -> Bool), element : T, fromInclusive : Nat) : ?Nat { - if (fromInclusive >= size(self)) Prim.trap "List index out of bounds in nextIndexOf"; - - let (blockIndex, elementIndex) = locate(fromInclusive); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = blockIndex; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return null; - - var j = if (i == blockIndex) elementIndex else 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (equal(x, element)) return ?indexByBlockElement(i, j); - case null return null - }; - j += 1 - }; - i += 1 - }; - null - }; - - /// Finds the last index of `element` in `list` using equality of elements defined - /// by `equal`. Returns `null` if `element` is not found. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 2, 3, 4, 2, 2]); - /// - /// assert List.lastIndexOf(list, Nat.equal, 2) == ?5; - /// assert List.lastIndexOf(list, Nat.equal, 5) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// *Runtime and space assumes that `equal` runs in `O(1)` time and space. - public func lastIndexOf(self : List, equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = prevIndexOf( - self, - equal, - element, - size(self) - ); - - /// Returns the index of the previous occurence of `element` in the `list` starting from the `from` index (exclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let list = List.fromArray(['c', 'o', 'f', 'f', 'e', 'e']); - /// assert List.prevIndexOf(list, Char.equal, 'c', List.size(list)) == ?0; - /// assert List.prevIndexOf(list, Char.equal, 'e', List.size(list)) == ?5; - /// assert List.prevIndexOf(list, Char.equal, 'e', 5) == ?4; - /// assert List.prevIndexOf(list, Char.equal, 'e', 4) == null; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func prevIndexOf(self : List, equal : (implicit : (T, T) -> Bool), element : T, fromExclusive : Nat) : ?Nat { - if (fromExclusive > size(self)) Prim.trap "List index out of bounds in prevIndexOf"; - - let blocks = self.blocks; - let (blockIndex, elementIndex) = locate(fromExclusive); - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) if (equal(x, element)) return ?indexByBlockElement(i, j); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - }; - - null - }; - - /// Returns the first value in `list` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let list = List.fromArray([1, 9, 4, 8]); - /// let found = List.find(list, func(x) { x > 8 }); - /// assert found == ?9; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func find(self : List, predicate : T -> Bool) : ?T { - Option.map(findIndex(self, predicate), func(i) = at(self, i)) - }; - - /// Finds the index of the first element in `list` for which `predicate` is true. - /// Returns `null` if no such element is found. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.findIndex(list, func(i) { i % 2 == 0 }) == ?1; - /// assert List.findIndex(list, func(i) { i > 5 }) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func findIndex(self : List, predicate : T -> Bool) : ?Nat { - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return null; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (predicate(x)) return ?indexByBlockElement(i, j); - case null return null - }; - j += 1 - }; - i += 1 - }; - null - }; - - /// Finds the index of the last element in `list` for which `predicate` is true. - /// Returns `null` if no such element is found. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.findLastIndex(list, func(i) { i % 2 == 0 }) == ?3; - /// assert List.findLastIndex(list, func(i) { i > 5 }) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func findLastIndex(self : List, predicate : T -> Bool) : ?Nat { - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) if (predicate(x)) return ?indexByBlockElement(i, j); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - }; - - null - }; - - /// Performs binary search on a sorted list to find the index of the `element`. - /// Returns `#found(index)` if the element is found, or `#insertionIndex(index)` with the index - /// where the element would be inserted according to the ordering if not found. - /// - /// If there are multiple equal elements, no guarantee is made about which index is returned. - /// The list must be sorted in ascending order according to the `compare` function. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 3, 5, 7, 9, 11]); - /// assert List.binarySearch(list, Nat.compare, 5) == #found(2); - /// assert List.binarySearch(list, Nat.compare, 6) == #insertionIndex(3); - /// ``` - /// - /// Runtime: `O(log(size))` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `compare` runs in `O(1)` time and space. - public func binarySearch(self : List, compare : (implicit : (T, T) -> Types.Order), element : T) : { - #found : Nat; - #insertionIndex : Nat - } { - // We call all data blocks of the same capacity an "epoch". We number the epochs 0,1,2,... - // A data block is in epoch e iff the data block has capacity 2 ** e. - // Each epoch starting with epoch 1 spans exactly two super blocks. - // Super block s falls in epoch ceil(s/2). - // Each epoch except e=0 contains 3 * 2 ** (e - 1) data blocks - - let blocks = self.blocks; - let b = self.blockIndex - (if (self.elementIndex == 0) 1 else 0) : Nat; - - // block index x such that blocks[x][0] <= element - let lessOrEqual = do { - // epoch of the last data block - let epoch = 32 - Nat32.bitcountLeadingZero(Nat32.fromNat(b) / 3); - // initially block index is the first in the epoch - var lessOrEqual = Nat32.toNat((1 << epoch) / 2); - - // lessOrEqual * 3 is always the first data block in an epoch - // while the first element of the first data block in an epoch is actually grater then element go to the previous epoch - // as the last epoch is half of the array we each iteration of the search divides the interval in four - while (lessOrEqual != 0 and compare(Option.unwrap(blocks[lessOrEqual * 3][0]), element) == #greater) { - lessOrEqual /= 2 - }; - - lessOrEqual * 3 - }; - - // Linear search in e=0, there are just two elements - if (lessOrEqual == 0) { - let to = Nat.min(size(self), 2); - for (i in Nat.range(0, to)) { - let x = at(self, i); - switch (compare(x, element)) { - case (#less) {}; - case (#equal) return #found(i); - case (#greater) return #insertionIndex(i) - } - }; - return #insertionIndex(to) - }; - - // binary search the blockIndex in [left, right) - let blockIndex = do { - // guarateed less or equal to element - var left = lessOrEqual; - // right is either outside of the array or greater than element - var right = Nat.min(b + 1, lessOrEqual * 2); - while (right - left : Nat > 1) { - let mid = (left + right) / 2; - switch (compare(Option.unwrap(blocks[mid][0]), element)) { - case (#less) left := mid; - case (#greater) right := mid; - case (#equal) return #found(indexByBlockElement(mid, 0)) - } - }; - left - }; - - // binary search the elementIndex - let elementIndex = do { - let block = blocks[blockIndex]; - var left = 0; - var right = if (blockIndex == self.blockIndex) self.elementIndex else block.size(); - while (left != right) { - let mid = (left + right) / 2; - switch (compare(Option.unwrap(block[mid]), element)) { - case (#less) left := mid + 1; - case (#greater) right := mid; - case (#equal) return #found(indexByBlockElement(blockIndex, mid)) - } - }; - left - }; - - #insertionIndex(indexByBlockElement(blockIndex, elementIndex)) - }; - - /// Returns true iff every element in `list` satisfies `predicate`. - /// In particular, if `list` is empty the function returns `true`. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.all(list, func x { x > 1 }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func all(self : List, predicate : T -> Bool) : Bool { - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return true; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (not predicate(x)) return false; - case null return true - }; - j += 1 - }; - i += 1 - }; - true - }; - - /// Returns true iff some element in `list` satisfies `predicate`. - /// In particular, if `list` is empty the function returns `false`. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.any(list, func x { x > 3 }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func any(self : List, predicate : T -> Bool) : Bool = findIndex(self, predicate) != null; - - /// Returns an Iterator (`Iter`) over the elements of a List. - /// Iterator provides a single method `next()`, which returns - /// elements in order, or `null` when out of elements to iterate over. - /// - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// - /// var sum = 0; - /// for (element in List.values(list)) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - public func values(self : List) : Types.Iter = object { - let blocks = self.blocks.size(); - var blockIndex = 0; - var elementIndex = 0; - var db : [var ?T] = self.blocks[blockIndex]; - var dbSize = db.size(); - - public func next() : ?T { - if (elementIndex == dbSize) { - blockIndex += 1; - if (blockIndex >= blocks) return null; - db := self.blocks[blockIndex]; - dbSize := db.size(); - if (dbSize == 0) return null; - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?x) { - elementIndex += 1; - return ?x - }; - case (_) return null - } - } - }; - - /// Returns an Iterator (`Iter`) over the items (index-value pairs) in the list. - /// Each item is a tuple of `(index, value)`. The iterator provides a single method - /// `next()` which returns elements in order, or `null` when out of elements. - /// - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// assert Iter.toArray(List.enumerate(list)) == [(0, 10), (1, 11), (2, 12)]; - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - /// - /// Warning: Allocates memory on the heap to store ?(Nat, T). - public func enumerate(self : List) : Types.Iter<(Nat, T)> = object { - let blocks = self.blocks.size(); - var blockIndex = 0; - var elementIndex = 0; - var size = 0; - var db : [var ?T] = [var]; - var i = 0; - - public func next() : ?(Nat, T) { - if (elementIndex == size) { - blockIndex += 1; - if (blockIndex >= blocks) return null; - db := self.blocks[blockIndex]; - size := db.size(); - if (size == 0) return null; - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?x) { - let ret = ?(i, x); - elementIndex += 1; - i += 1; - return ret - }; - case (_) return null - } - } - }; - - /// Returns an Iterator (`Iter`) over the elements of the list in reverse order. - /// The iterator provides a single method `next()` which returns elements from - /// last to first, or `null` when out of elements. - /// - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// - /// var sum = 0; - /// for (element in List.reverseValues(list)) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - public func reverseValues(self : List) : Types.Iter = object { - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex; - var db : [var ?T] = if (blockIndex < self.blocks.size()) { - self.blocks[blockIndex] - } else { [var] }; - - public func next() : ?T { - if (elementIndex != 0) { - elementIndex -= 1 - } else { - blockIndex -= 1; - if (blockIndex == 0) return null; - db := self.blocks[blockIndex]; - elementIndex := db.size() - 1 - }; - - db[elementIndex] - } - }; - - /// Returns an Iterator (`Iter`) over the items in reverse order, i.e. pairs of index and value. - /// Iterator provides a single method `next()`, which returns - /// elements in reverse order, or `null` when out of elements to iterate over. - /// - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// assert Iter.toArray(List.reverseEnumerate(list)) == [(2, 12), (1, 11), (0, 10)]; - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - /// - /// Warning: Allocates memory on the heap to store ?(T, Nat). - public func reverseEnumerate(self : List) : Types.Iter<(Nat, T)> = object { - var i = size(self); - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex; - var db : [var ?T] = if (blockIndex < self.blocks.size()) { - self.blocks[blockIndex] - } else { [var] }; - - public func next() : ?(Nat, T) { - if (elementIndex != 0) { - elementIndex -= 1 - } else { - blockIndex -= 1; - if (blockIndex == 0) return null; - db := self.blocks[blockIndex]; - elementIndex := db.size() - 1 - }; - switch (db[elementIndex]) { - case (?x) { - i -= 1; - return ?(i, x) - }; - case (_) Prim.trap INTERNAL_ERROR - } - } - }; - - /// Returns an Iterator (`Iter`) over the indices (keys) of the list. - /// The iterator provides a single method `next()` which returns indices - /// from 0 to size-1, or `null` when out of elements. - /// - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let list = List.empty(); - /// List.add(list, "A"); - /// List.add(list, "B"); - /// List.add(list, "C"); - /// Iter.toArray(List.keys(list)) // [0, 1, 2] - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - public func keys(self : List) : Types.Iter = Nat.range(0, size(self)); - - /// Creates a new List containing all elements from the provided iterator. - /// Elements are added in the order they are returned by the iterator. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [1, 1, 1]; - /// let iter = array.vals(); - /// - /// let list = List.fromIter(iter); - /// assert Iter.toArray(List.values(list)) == [1, 1, 1]; - /// ``` - /// - /// Runtime: `O(size)` - public func fromIter(iter : Types.Iter) : List { - let list = empty(); - for (element in iter) add(list, element); - list - }; - - /// Convert an iterator to a new mutable List. - /// Elements are added in the order they are returned by the iterator. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [1, 1, 1]; - /// let iter = array.vals(); - /// - /// let list = iter.toList(); - /// assert Iter.toArray(List.values(list)) == [1, 1, 1]; - /// ``` - /// - /// Runtime: `O(size)` - public func toList(self : Types.Iter) : List { - fromIter(self) - }; - - /// Appends all elements from `added` to the end of `list`. - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2]); - /// let added = List.fromArray([3, 4]); - /// List.append(list, added); - /// assert List.toArray(list) == [1, 2, 3, 4]; - /// ``` - /// - /// Runtime: `O(size(added))` - /// - /// Space: `O(size(added))` - public func append(self : List, added : List) { - reserve(self, size(added)); - - let blocks = added.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) addUnsafe(self, x); - case null return - }; - j += 1 - }; - i += 1 - } - }; - - /// Adds all elements from the provided iterator to the end of the list. - /// Elements are added in the order they are returned by the iterator. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [1, 1, 1]; - /// let iter = array.vals(); - /// let list = List.repeat(2, 1); - /// - /// List.addAll(list, iter); - /// assert Iter.toArray(List.values(list)) == [2, 1, 1, 1]; - /// ``` - /// - /// The maximum number of elements in a `List` is 2^32. - /// - /// Runtime: `O(size)`, where n is the size of iter. - public func addAll(self : List, iter : Types.Iter) { - for (element in iter) add(self, element) - }; - - /// Creates a new immutable array containing all elements from the list. - /// Elements appear in the same order as in the list. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3]); - /// - /// assert List.toArray(list) == [1, 2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - public func toArray(self : List) : [T] { - var blockIndex = 0; - var elementIndex = 0; - var sz = 0; - var db : [var ?T] = [var]; - - func generator(_ : Nat) : T { - if (elementIndex == sz) { - blockIndex += 1; - db := self.blocks[blockIndex]; - sz := db.size(); - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?x) { - elementIndex += 1; - return x - }; - case (_) Prim.trap INTERNAL_ERROR - } - }; - - Array.tabulate(size(self), generator) - }; - - /// Creates a List containing elements from an Array. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [2, 3]; - /// let list = List.fromArray(array); - /// assert Iter.toArray(List.values(list)) == [2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - public func fromArray(array : [T]) : List { - let (blockIndex, elementIndex) = locate(array.size()); - - let blocks = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) { blockIndex - 1 } else blockIndex)); - let dataBlocks = VarArray.repeat<[var ?T]>([var], blocks); - - var i = 1; - var pos = 0; - - while (i < blockIndex) { - let len = dataBlockSize(i); - dataBlocks[i] := VarArray.tabulate(len, func i = ?array[pos + i]); - pos += len; - i += 1 - }; - if (elementIndex != 0 and blockIndex < blocks) { - dataBlocks[i] := VarArray.tabulate( - dataBlockSize(i), - func i = if (i < elementIndex) ?array[pos + i] else null - ) - }; - - { - var blocks = dataBlocks; - var blockIndex = blockIndex; - var elementIndex = elementIndex - } - }; - - /// Creates a new mutable array containing all elements from the list. - /// Elements appear in the same order as in the list. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// let varArray = List.toVarArray(list); - /// assert Array.fromVarArray(varArray) == [1, 2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - public func toVarArray(self : List) : [var T] { - let ?fs = first(self) else return [var]; - - let array = VarArray.repeat(fs, size(self)); - - var index = 0; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return array; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) array[index] := x; - case null return array - }; - j += 1; - index += 1 - }; - i += 1 - }; - array - }; - - /// Creates a new List containing all elements from the mutable array. - /// Elements appear in the same order as in the array. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [var 2, 3]; - /// let list = List.fromVarArray(array); - /// assert Iter.toArray(List.values(list)) == [2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - public func fromVarArray(array : [var T]) : List { - let (blockIndex, elementIndex) = locate(array.size()); - - let blocks = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) { blockIndex - 1 } else blockIndex)); - let dataBlocks = VarArray.repeat<[var ?T]>([var], blocks); - - func makeBlock(array : [var T], p : Nat, len : Nat, fill : Nat) : [var ?T] { - let block = VarArray.repeat(null, len); - var j = 0; - var pos = p; - while (j < fill) { - block[j] := ?array[pos]; - j += 1; - pos += 1 - }; - block - }; - - var i = 1; - var pos = 0; - - while (i < blockIndex) { - let len = dataBlockSize(i); - dataBlocks[i] := makeBlock(array, pos, len, len); - pos += len; - i += 1 - }; - if (elementIndex != 0) { - dataBlocks[i] := makeBlock(array, pos, dataBlockSize(i), elementIndex) - }; - - { - var blocks = dataBlocks; - var blockIndex = blockIndex; - var elementIndex = elementIndex - } - }; - - /// Returns the first element of `list`, or `null` if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// assert List.first(List.fromArray([1, 2, 3])) == ?1; - /// assert List.first(List.empty()) == null; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func first(self : List) : ?T { - if (self.blockIndex == 1) null else self.blocks[1][0] - }; - - /// Returns the last element of `list`, or `null` if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// assert List.last(List.fromArray([1, 2, 3])) == ?3; - /// assert List.last(List.empty()) == null; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func last(self : List) : ?T { - let e = self.elementIndex; - if (e > 0) return self.blocks[self.blockIndex][e - 1]; - - let b = self.blockIndex - 1 : Nat; - if (b == 0) null else { - let block = self.blocks[b]; - block[block.size() - 1] - } - }; - - /// Applies `f` to each element in `list`. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// List.forEach(list, func(x) { - /// Debug.print(Nat.toText(x)); // prints each element in list - /// }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEach(self : List, f : T -> ()) { - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) f(x); - case null return - }; - j += 1 - }; - i += 1 - } - }; - - /// Applies `f` to each item `(i, x)` in `list` where `i` is the key - /// and `x` is the value. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// List.forEachEntry(list, func (i,x) { - /// // prints each item (i,x) in list - /// Debug.print(Nat.toText(i) # Nat.toText(x)); - /// }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEachEntry(self : List, f : (Nat, T) -> ()) { - var index = 0; - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) f(index, x); - case null return - }; - j += 1; - index += 1 - }; - i += 1 - } - }; - - func actualInterval(fromInclusive : Int, toExclusive : Int, size : Nat) : (Nat, Nat) { - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - (Prim.abs(startInt), Prim.abs(endInt)) - }; - - /// Returns an iterator over a slice of `list` starting at `fromInclusive` up to (but not including) `toExclusive`. - /// - /// Negative indices are relative to the end of the list. For example, `-1` corresponds to the last element in the list. - /// - /// If the indices are out of bounds, they are clamped to the list bounds. - /// If the first index is greater than the second, the function returns an empty iterator. - /// - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4, 5]); - /// let iter1 = List.range(list, 3, List.size(list)); - /// assert iter1.next() == ?4; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// let iter2 = List.range(list, 3, -1); - /// assert iter2.next() == ?4; - /// assert iter2.next() == null; - /// - /// let iter3 = List.range(list, 0, 0); - /// assert iter3.next() == null; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func range(self : List, fromInclusive : Int, toExclusive : Int) : Types.Iter = object { - let (start, end) = actualInterval(fromInclusive, toExclusive, size(self)); - let blocks = self.blocks.size(); - var blockIndex = 0; - var elementIndex = 0; - if (start != 0) { - let (block, element) = locate(start - 1); - blockIndex := block; - elementIndex := element + 1 - }; - var db : [var ?T] = self.blocks[blockIndex]; - var dbSize = db.size(); - var index = fromInclusive; - - public func next() : ?T { - if (index >= end) return null; - index += 1; - - if (elementIndex == dbSize) { - blockIndex += 1; - if (blockIndex >= blocks) return null; - db := self.blocks[blockIndex]; - dbSize := db.size(); - if (dbSize == 0) return null; - elementIndex := 0 - }; - let ret = db[elementIndex]; - elementIndex += 1; - ret - } - }; - - func sliceToArrayBase(self : List, start : Nat) : { - next(i : Nat) : T - } = object { - var blockIndex = 0; - var elementIndex = 0; - if (start != 0) { - let (block, element) = locate(start - 1); - blockIndex := block; - elementIndex := element + 1 - }; - var db : [var ?T] = self.blocks[blockIndex]; - var dbSize = db.size(); - - public func next(i : Nat) : T { - if (elementIndex == dbSize) { - blockIndex += 1; - db := self.blocks[blockIndex]; - dbSize := db.size(); - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?x) { - elementIndex += 1; - return x - }; - case null Prim.trap INTERNAL_ERROR - } - } - }; - - /// Returns a new array containing elements from `list` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// let array = List.fromArray([1, 2, 3, 4, 5]); - /// - /// let slice1 = List.sliceToArray(array, 1, 4); - /// assert slice1 == [2, 3, 4]; - /// - /// let slice2 = List.sliceToArray(array, 1, -1); - /// assert slice2 == [2, 3, 4]; - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToArray(self : List, fromInclusive : Int, toExclusive : Int) : [T] { - let (start, end) = actualInterval(fromInclusive, toExclusive, size(self)); - Array.tabulate(end - start, sliceToArrayBase(self, start).next) - }; - - /// Returns a new var array containing elements from `list` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// import VarArray "mo:core/VarArray"; - /// import Nat "mo:core/Nat"; - /// - /// let array = List.fromArray([1, 2, 3, 4, 5]); - /// - /// let slice1 = List.sliceToVarArray(array, 1, 4); - /// assert VarArray.equal(slice1, [var 2, 3, 4], Nat.equal); - /// - /// let slice2 = List.sliceToVarArray(array, 1, -1); - /// assert VarArray.equal(slice2, [var 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToVarArray(self : List, fromInclusive : Int, toExclusive : Int) : [var T] { - let (start, end) = actualInterval(fromInclusive, toExclusive, size(self)); - VarArray.tabulate(end - start, sliceToArrayBase(self, start).next) - }; - - /// Like `forEachEntryRev` but iterates through the list in reverse order, - /// from end to beginning. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// List.reverseForEachEntry(list, func (i,x) { - /// // prints each item (i,x) in list - /// Debug.print(Nat.toText(i) # Nat.toText(x)); - /// }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func reverseForEachEntry(self : List, f : (Nat, T) -> ()) { - var index = 0; - - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) f(index, x); - case null Prim.trap INTERNAL_ERROR - }; - index += 1 - }; - i -= 1 - } - }; - - /// Applies `f` to each element in `list` in reverse order. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// List.reverseForEach(list, func (x) { - /// Debug.print(Nat.toText(x)); // prints each element in list in reverse order - /// }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func reverseForEach(self : List, f : T -> ()) { - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) f(x); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - } - }; - - /// Executes the closure over a slice of `list` starting at `fromInclusive` up to (but not including) `toExclusive`. - /// - /// ```motoko include=import - /// import Debug "mo:core/Debug"; - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 2, 3, 4, 5]); - /// List.forEachInRange(list, func x = Debug.print(Nat.toText(x)), 1, 2); // prints 2 and 3 - /// ``` - /// - /// Runtime: `O(toExclusive - fromExclusive)` - /// - /// Space: `O(1)` - public func forEachInRange(self : List, f : T -> (), fromInclusive : Nat, toExclusive : Nat) { - if (not (fromInclusive <= toExclusive and toExclusive <= size(self))) Prim.trap("Invalid range"); - - func traverseBlock(block : [var ?T], f : T -> (), from : Nat, to : Nat) { - var i = from; - while (i < to) { - switch (block[i]) { - case (?value) f(value); - case null Prim.trap(INTERNAL_ERROR) - }; - i += 1 - } - }; - - let (fromBlock, fromElement) = locate(fromInclusive); - let (toBlock, toElement) = locate(toExclusive); - - let blocks = self.blocks; - let sz = blocks.size(); - - if (fromBlock == toBlock) { - if (fromBlock < sz) traverseBlock(blocks[fromBlock], f, fromElement, toElement); - return - }; - - traverseBlock(blocks[fromBlock], f, fromElement, blocks[fromBlock].size()); - - var i = fromBlock + 1; - let to = Nat.min(toBlock, sz); - while (i < to) { - traverseBlock(blocks[i], f, 0, blocks[i].size()); - i += 1 - }; - - if (toBlock < sz) traverseBlock(blocks[toBlock], f, 0, toElement) - }; - - /// Returns true if the list contains the specified element according to the provided - /// equality function. Uses the provided `equal` function to compare elements. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 2); - /// List.add(list, 0); - /// List.add(list, 3); - /// - /// assert List.contains(list, Nat.equal, 2); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func contains(self : List, equal : (implicit : (T, T) -> Bool), element : T) : Bool { - Option.isSome(indexOf(self, equal, element)) - }; - - /// Returns the greatest element in the list according to the ordering defined by `compare`. - /// Returns `null` if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// - /// assert List.max(list, Nat.compare) == ?2; - /// assert List.max(List.empty(), Nat.compare) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func max(self : List, compare : (implicit : (T, T) -> Types.Order)) : ?T { - var maxSoFar : T = switch (first(self)) { - case (?x) x; - case null return null - }; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return ?maxSoFar; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) switch (compare(x, maxSoFar)) { - case (#greater) maxSoFar := x; - case _ {} - }; - case null return ?maxSoFar - }; - j += 1 - }; - i += 1 - }; - - ?maxSoFar - }; - - /// Returns the least element in the list according to the ordering defined by `compare`. - /// Returns `null` if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// - /// assert List.min(list, Nat.compare) == ?1; - /// assert List.min(List.empty(), Nat.compare) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func min(self : List, compare : (implicit : (T, T) -> Types.Order)) : ?T { - var minSoFar : T = switch (first(self)) { - case (?x) x; - case null return null - }; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return ?minSoFar; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) switch (compare(x, minSoFar)) { - case (#less) minSoFar := x; - case _ {} - }; - case null return ?minSoFar - }; - j += 1 - }; - i += 1 - }; - - ?minSoFar - }; - - /// Tests if two lists are equal by comparing their elements using the provided `equal` function. - /// Returns true if and only if both lists have the same size and all corresponding elements - /// are equal according to the provided function. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list1 = List.fromArray([1,2]); - /// let list2 = List.empty(); - /// List.add(list2, 1); - /// List.add(list2, 2); - /// - /// assert List.equal(list1, list2, Nat.equal); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func equal(self : List, other : List, equal : (implicit : (T, T) -> Bool)) : Bool { - if (size(self) != size(other)) return false; - - let blocks1 = self.blocks; - let blocks2 = other.blocks; - let blockCount = Nat.min(blocks1.size(), blocks2.size()); - - var i = 1; - while (i < blockCount) { - let db1 = blocks1[i]; - let db2 = blocks2[i]; - let sz = Nat.min(db1.size(), db2.size()); - if (sz == 0) return true; - - var j = 0; - while (j < sz) { - switch (db1[j], db2[j]) { - case (?x, ?y) if (not equal(x, y)) return false; - case (_, _) return true - }; - j += 1 - }; - i += 1 - }; - return true - }; - - /// Compares two lists lexicographically using the provided `compare` function. - /// Elements are compared pairwise until a difference is found or one list ends. - /// If all elements compare equal, the shorter list is considered less than the longer list. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list1 = List.fromArray([0, 1]); - /// let list2 = List.fromArray([2]); - /// let list3 = List.fromArray([0, 1, 2]); - /// - /// assert List.compare(list1, list2, Nat.compare) == #less; - /// assert List.compare(list1, list3, Nat.compare) == #less; - /// assert List.compare(list2, list3, Nat.compare) == #greater; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func compare(self : List, other : List, compare : (implicit : (T, T) -> Types.Order)) : Types.Order { - let blocks1 = self.blocks; - let blocks2 = other.blocks; - let blockCount = Nat.min(blocks1.size(), blocks2.size()); - - var i = 1; - label l while (i < blockCount) { - let db1 = blocks1[i]; - let db2 = blocks2[i]; - let sz = Nat.min(db1.size(), db2.size()); - if (sz == 0) break l; - - var j = 0; - while (j < sz) { - switch (db1[j], db2[j]) { - case (?x, ?y) switch (compare(x, y)) { - case (#less) return #less; - case (#greater) return #greater; - case _ {} - }; - case (_, _) break l - }; - j += 1 - }; - i += 1 - }; - return Nat.compare(size(self), size(other)) - }; - - /// Creates a textual representation of `list`, using `toText` to recursively - /// convert the elements into Text. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1,2,3,4]); - /// - /// assert List.toText(list, Nat.toText) == "List[1, 2, 3, 4]"; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `toText` runs in O(1) time and space. - public func toText(self : List, toText : (implicit : T -> Text)) : Text { - var text = switch (first(self)) { - case (?x) toText(x); - case null "" - }; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - label l while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) break l; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) text #= ", " # toText(x); - case null break l - }; - j += 1 - }; - i += 1 - }; - - "List[" # text # "]" - }; - - /// Collapses the elements in `list` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1,2,3]); - /// - /// assert List.foldLeft(list, "", func (acc, x) { acc # Nat.toText(x)}) == "123"; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `combine` runs in O(1)` time and space. - public func foldLeft(self : List, base : A, combine : (A, T) -> A) : A { - var accumulation = base; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return accumulation; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) accumulation := combine(accumulation, x); - case null return accumulation - }; - j += 1 - }; - i += 1 - }; - accumulation - }; - - /// Collapses the elements in `list` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1,2,3]); - /// - /// assert List.foldRight(list, "", func (x, acc) { Nat.toText(x) # acc }) == "123"; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `combine` runs in O(1)` time and space. - public func foldRight(self : List, base : A, combine : (T, A) -> A) : A { - var accumulation = base; - - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) accumulation := combine(x, accumulation); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - }; - - accumulation - }; - - /// Reverses the order of elements in `list` by overwriting in place. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let list = List.fromArray([1,2,3]); - /// - /// List.reverseInPlace(list); - /// assert Iter.toArray(List.values(list)) == [3, 2, 1]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func reverseInPlace(self : List) { - let vsize = size(self); - if (vsize <= 1) return; - - let (finalBlock, finalElement) = locate(vsize / 2); - - let blocks = self.blocks; - - var blockIndexBack = self.blockIndex; - var elementIndexBack = self.elementIndex; - var dbBack : [var ?T] = if (blockIndexBack < self.blocks.size()) { - self.blocks[blockIndexBack] - } else { [var] }; - - var i = 1; - var index = 0; - while (i <= finalBlock) { - let db = blocks[i]; - let sz = if (i == finalBlock) finalElement else db.size(); - - var j = 0; - while (j < sz) { - if (elementIndexBack == 0) { - blockIndexBack -= 1; - dbBack := self.blocks[blockIndexBack]; - elementIndexBack := dbBack.size() - 1 - } else { - elementIndexBack -= 1 - }; - - let temp = db[j]; - db[j] := dbBack[elementIndexBack]; - dbBack[elementIndexBack] := temp; - - j += 1; - index += 1 - }; - i += 1 - } - }; - - /// Returns a new List with the elements from `list` in reverse order. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let list = List.fromArray([1,2,3]); - /// - /// let rlist = List.reverse(list); - /// assert Iter.toArray(List.values(rlist)) == [3, 2, 1]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func reverse(self : List) : List { - let rlist = repeatInternal(null, size(self)); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var blockIndexBack = rlist.blockIndex; - var elementIndexBack = rlist.elementIndex; - var dbBack : [var ?T] = if (blockIndexBack < rlist.blocks.size()) { - rlist.blocks[blockIndexBack] - } else { [var] }; - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return rlist; - - var j = 0; - while (j < sz) { - if (elementIndexBack == 0) { - blockIndexBack -= 1; - if (blockIndexBack == 0) return rlist; - dbBack := rlist.blocks[blockIndexBack]; - elementIndexBack := dbBack.size() - 1 - } else { - elementIndexBack -= 1 - }; - - dbBack[elementIndexBack] := db[j]; - j += 1 - }; - i += 1 - }; - rlist - }; - - /// Returns true if and only if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([2,0,3]); - /// assert not List.isEmpty(list); - /// assert List.isEmpty(List.empty()); - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func isEmpty(self : List) : Bool { - self.blockIndex == 1 - }; - - /// Unsafe iterator starting from `start`. - /// - /// Example: - /// ``` - /// let list = List.fromArray([1, 2, 3, 4, 5]); - /// let reader = List.reader(list, 2); - /// assert reader() == 3; - /// assert reader() == 4; - /// assert reader() == 5; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func reader(self : List, start : Nat) : () -> T { - var blockIndex = 0; - var elementIndex = 0; - if (start != 0) { - let (block, element) = locate(start - 1); - blockIndex := block; - elementIndex := element + 1 - }; - var db : [var ?T] = self.blocks[blockIndex]; - var dbSize = db.size(); - func next() : T { - // Note: next() traps when reading beyond end of list - if (elementIndex == dbSize) { - blockIndex += 1; - db := self.blocks[blockIndex]; - dbSize := db.size(); - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?ret) { - elementIndex += 1; - return ret - }; - case (_) Prim.trap("List.reader(): out of bounds") - } - }; - next - }; - -} diff --git a/.mops/core@2.3.1/src/Map.mo b/.mops/core@2.3.1/src/Map.mo deleted file mode 100644 index 6e10175..0000000 --- a/.mops/core@2.3.1/src/Map.mo +++ /dev/null @@ -1,2672 +0,0 @@ -/// An imperative key-value map based on order/comparison of the keys. -/// The map data structure type is stable and can be used for orthogonal persistence. -/// -/// Example: -/// ```motoko -/// import Map "mo:core/Map"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// // creation -/// let map = Map.empty(); -/// // insertion -/// Map.add(map, Nat.compare, 0, "Zero"); -/// // retrieval -/// assert Map.get(map, Nat.compare, 0) == ?"Zero"; -/// assert Map.get(map, Nat.compare, 1) == null; -/// // removal -/// Map.remove(map, Nat.compare, 0); -/// assert Map.isEmpty(map); -/// } -/// ``` -/// -/// The internal implementation is a B-tree with order 32. -/// -/// Performance: -/// * Runtime: `O(log(n))` worst case cost per insertion, removal, and retrieval operation. -/// * Space: `O(n)` for storing the entire map. -/// `n` denotes the number of key-value entries stored in the map. - -// Data structure implementation is courtesy of Byron Becker. -// Source: https://github.com/canscale/StableHeapBTreeMap -// Copyright (c) 2022 Byron Becker. -// Distributed under Apache 2.0 license. -// With adjustments by the Motoko team. - -import PureMap "pure/Map"; -import Types "Types"; -import Iter "Iter"; -import Order "Order"; -import VarArray "VarArray"; -import Runtime "Runtime"; -import Stack "Stack"; -import Option "Option"; -import BTreeHelper "internal/BTreeHelper"; - -module { - let btreeOrder = 32; // Should be >= 4 and <= 512. - - public type Map = Types.Map; - - type Node = Types.Map.Node; - type Data = Types.Map.Data; - type Internal = Types.Map.Internal; - type Leaf = Types.Map.Leaf; - - /// Convert the mutable key-value map to an immutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import PureMap "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), Nat.compare); - /// let pureMap = Map.toPure(map, Nat.compare); - /// assert Iter.toArray(PureMap.entries(pureMap)) == Iter.toArray(Map.entries(map)) - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - /// @deprecated M0235 - public func toPure(self : Map, compare : (implicit : (K, K) -> Order.Order)) : PureMap.Map { - PureMap.fromIter(entries(self), compare) - }; - - /// Convert an immutable key-value map to a mutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import PureMap "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let pureMap = PureMap.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), Nat.compare); - /// let map = Map.fromPure(pureMap, Nat.compare); - /// assert Iter.toArray(Map.entries(map)) == Iter.toArray(PureMap.entries(pureMap)) - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func fromPure(map : PureMap.Map, compare : (implicit : (K, K) -> Order.Order)) : Map { - fromIter(PureMap.entries(map), compare) - }; - - /// Create a copy of the mutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let originalMap = Map.fromIter( - /// [(1, "One"), (2, "Two"), (3, "Three")].values(), Nat.compare); - /// let clonedMap = Map.clone(originalMap); - /// Map.add(originalMap, Nat.compare, 4, "Four"); - /// assert Map.size(clonedMap) == 3; - /// assert Map.size(originalMap) == 4; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func clone(self : Map) : Map { - { - var root = cloneNode(self.root); - var size = self.size - } - }; - - /// Create a new empty mutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// assert Map.size(map) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Map { - { - var root = #leaf({ - data = { - kvs = VarArray.repeat(null, btreeOrder - 1); - var count = 0 - } - }); - var size = 0 - } - }; - - /// Create a new mutable key-value map with a single entry. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.singleton(0, "Zero"); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero")]; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func singleton(key : K, value : V) : Map { - let kvs = VarArray.repeat(null, btreeOrder - 1); - kvs[0] := ?(key, value); - { - var root = #leaf { data = { kvs; var count = 1 } }; - var size = 1 - } - }; - - /// Delete all the entries in the key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert Map.size(map) == 3; - /// - /// Map.clear(map); - /// assert Map.size(map) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func clear(self : Map) { - let emptyMap = empty(); - self.root := emptyMap.root; - self.size := 0 - }; - - /// Determines whether a key-value map is empty. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert not Map.isEmpty(map); - /// Map.clear(map); - /// assert Map.isEmpty(map); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func isEmpty(self : Map) : Bool { - self.size == 0 - }; - - /// Return the number of entries in a key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert Map.size(map) == 3; - /// Map.clear(map); - /// assert Map.size(map) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func size(self : Map) : Nat { - self.size - }; - - /// Test whether two imperative maps have equal entries. - /// Both maps have to be constructed by the same comparison function. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let map1 = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// let map2 = Map.clone(map1); - /// - /// assert Map.equal(map1, map2, Nat.compare, Text.equal); - /// Map.clear(map2); - /// assert not Map.equal(map1, map2, Nat.compare, Text.equal); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func equal(self : Map, other : Map, compare : (implicit : (K, K) -> Types.Order), equal : (implicit : (V, V) -> Bool)) : Bool { - if (size(self) != size(other)) { - return false - }; - let iterator1 = entries(self); - let iterator2 = entries(other); - loop { - let next1 = iterator1.next(); - let next2 = iterator2.next(); - switch (next1, next2) { - case (null, null) { - return true - }; - case (?(key1, value1), ?(key2, value2)) { - if ( - not (compare(key1, key2) == #equal) or - not equal(value1, value2) - ) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Tests whether the map contains the provided key. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert Map.containsKey(map, Nat.compare, 1); - /// assert not Map.containsKey(map, Nat.compare, 3); - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func containsKey(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : Bool { - Option.isSome(get(self, compare, key)) - }; - - /// Get the value associated with key in the given map if present and `null` otherwise. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert Map.get(map, Nat.compare, 1) == ?"One"; - /// assert Map.get(map, Nat.compare, 3) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func get(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : ?V { - switch (self.root) { - case (#internal(internalNode)) { - getFromInternal(internalNode, compare, key) - }; - case (#leaf(leafNode)) { getFromLeaf(leafNode, compare, key) } - } - }; - - /// Given `map` ordered by `compare`, insert a new mapping from `key` to `value`. - /// Replaces any existing entry under `key`. - /// Returns true if the key is new to the map, otherwise false. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// assert Map.insert(map, Nat.compare, 0, "Zero"); - /// assert Map.insert(map, Nat.compare, 1, "One"); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One")]; - /// assert not Map.insert(map, Nat.compare, 0, "Nil"); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Nil"), (1, "One")] - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func insert(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : Bool { - switch (swap(self, compare, key, value)) { - case null true; - case _ false - } - }; - - /// Given `map` ordered by `compare`, add a mapping from `key` to `value` to `map`. - /// Replaces any existing entry for `key`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// - /// Map.add(map, Nat.compare, 0, "Zero"); - /// Map.add(map, Nat.compare, 1, "One"); - /// Map.add(map, Nat.compare, 0, "Nil"); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Nil"), (1, "One")] - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func add(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) { - ignore swap(self, compare, key, value) - }; - - /// Associates the value with the key in the map. - /// If the key is not yet present in the map, a new key-value pair is added and `null` is returned. - /// Otherwise, if the key is already present, the value is overwritten and the previous value is returned. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.singleton(1, "One"); - /// - /// assert Map.swap(map, Nat.compare, 0, "Zero") == null; - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One")]; - /// - /// assert Map.swap(map, Nat.compare, 0, "Nil") == ?"Zero"; - /// assert Iter.toArray(Map.entries(map)) == [(0, "Nil"), (1, "One")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func swap(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : ?V { - let insertResult = switch (self.root) { - case (#leaf(leafNode)) { - leafInsertHelper(leafNode, btreeOrder, compare, key, value) - }; - case (#internal(internalNode)) { - internalInsertHelper(internalNode, btreeOrder, compare, key, value) - } - }; - - switch (insertResult) { - case (#insert(ov)) { - switch (ov) { - // if inserted a value that was not previously there, increment the tree size counter - case null { self.size += 1 }; - case _ {} - }; - ov - }; - case (#promote({ kv; leftChild; rightChild })) { - let kvs = VarArray.repeat(null, btreeOrder - 1); - kvs[0] := ?kv; - let children = VarArray.repeat>(null, btreeOrder); - children[0] := ?leftChild; - children[1] := ?rightChild; - self.root := #internal({ - data = { - kvs; - var count = 1 - }; - children - }); - // promotion always comes from inserting a new element, so increment the tree size counter - self.size += 1; - - null - } - } - }; - - /// Overwrites the value of an existing key and returns the previous value. - /// If the key does not exist, it has no effect and returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.singleton(0, "Zero"); - /// - /// let prev1 = Map.replace(map, Nat.compare, 0, "Nil"); // overwrites the value for existing key. - /// assert prev1 == ?"Zero"; - /// assert Map.get(map, Nat.compare, 0) == ?"Nil"; - /// - /// let prev2 = Map.replace(map, Nat.compare, 1, "One"); // no effect, key is absent - /// assert prev2 == null; - /// assert Map.get(map, Nat.compare, 1) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func replace(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : ?V { - // TODO: Could be optimized in future - if (containsKey(self, compare, key)) { - swap(self, compare, key, value) - } else { - null - } - }; - - /// Delete an entry by its key in the map. - /// No effect if the key is not present. - /// - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (2, "Two"), (1, "One")].values(), - /// Nat.compare); - /// - /// Map.remove(map, Nat.compare, 1); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (2, "Two")]; - /// Map.remove(map, Nat.compare, 42); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - public func remove(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) { - ignore delete(self, compare, key) - }; - - /// Delete an existing entry by its key in the map. - /// Returns `true` if the key was present in the map, otherwise `false`. - /// - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (2, "Two"), (1, "One")].values(), - /// Nat.compare); - /// - /// assert Map.delete(map, Nat.compare, 1); // present, returns true - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (2, "Two")]; - /// - /// assert not Map.delete(map, Nat.compare, 42); // absent, returns false - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// @deprecated M0235 - public func delete(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : Bool { - switch (take(self, compare, key)) { - case null false; - case _ true - } - }; - - /// Removes any existing entry by its key in the map. - /// Returns the previous value of the key or `null` if the key was absent. - /// - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (2, "Two"), (1, "One")].values(), - /// Nat.compare); - /// - /// assert Map.take(map, Nat.compare, 0) == ?"Zero"; - /// assert Iter.toArray(Map.entries(map)) == [(1, "One"), (2, "Two")]; - /// - /// assert Map.take(map, Nat.compare, 3) == null; - /// assert Iter.toArray(Map.entries(map)) == [(1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// @deprecated M0235 - public func take(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : ?V { - let deletedValue = switch (self.root) { - case (#leaf(leafNode)) { - // TODO: think about how this can be optimized so don't have to do two steps (search and then insert)? - switch (NodeUtil.getKeyIndex(leafNode.data, compare, key)) { - case (#keyFound(deleteIndex)) { - leafNode.data.count -= 1; - let (_, deletedValue) = BTreeHelper.deleteAndShift<(K, V)>(leafNode.data.kvs, deleteIndex); - self.size -= 1; - ?deletedValue - }; - case _ { null } - } - }; - case (#internal(internalNode)) { - let deletedValueResult = switch (internalDeleteHelper(internalNode, btreeOrder, compare, key, false)) { - case (#delete(value)) { value }; - case (#mergeChild({ internalChild; deletedValue })) { - if (internalChild.data.count > 0) { - self.root := #internal(internalChild) - } - // This case will be hit if the BTree has order == 4 - // In this case, the internalChild has no keys (last key was merged with new child), so need to promote that merged child (its only child) - else { - self.root := switch (internalChild.children[0]) { - case (?node) { node }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.delete(), element deletion failed, due to a null replacement node error") - } - } - }; - deletedValue - } - }; - switch (deletedValueResult) { - // if deleted a value from the BTree, decrement the size - case (?deletedValue) { self.size -= 1 }; - case null {} - }; - deletedValueResult - } - }; - deletedValue - }; - - public func toArray(self : Map) : [(K, V)] { - Iter.toArray(entries(self)) - }; - - public func toVarArray(self : Map) : [var (K, V)] { - Iter.toVarArray(entries(self)) - }; - - /// Retrieves the key-value pair from the map with the maximum key. - /// If the map is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// - /// assert Map.maxEntry(map) == null; - /// - /// Map.add(map, Nat.compare, 0, "Zero"); - /// Map.add(map, Nat.compare, 2, "Two"); - /// Map.add(map, Nat.compare, 1, "One"); - /// - /// assert Map.maxEntry(map) == ?(2, "Two") - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func maxEntry(self : Map) : ?(K, V) { - reverseEntries(self).next() - }; - - /// Retrieves the key-value pair from the map with the minimum key. - /// If the map is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// - /// assert Map.minEntry(map) == null; - /// - /// Map.add(map, Nat.compare, 2, "Two"); - /// Map.add(map, Nat.compare, 0, "Zero"); - /// Map.add(map, Nat.compare, 1, "One"); - /// - /// assert Map.minEntry(map) == ?(0, "Zero") - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func minEntry(self : Map) : ?(K, V) { - entries(self).next() - }; - - /// Returns an iterator over the key-value pairs in the map, - /// traversing the entries in the ascending order of the keys. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// var sum = 0; - /// var text = ""; - /// for ((k, v) in Map.entries(map)) { sum += k; text #= v }; - /// assert sum == 3; - /// assert text == "ZeroOneTwo" - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func entries(self : Map) : Types.Iter<(K, V)> { - switch (self.root) { - case (#leaf(leafNode)) { return leafEntries(leafNode) }; - case (#internal(internalNode)) { internalEntries(internalNode) } - } - }; - - /// Returns an iterator over the key-value pairs in the map, - /// starting from a given key in ascending order. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (3, "Three"), (1, "One")].values(), Nat.compare); - /// assert Iter.toArray(Map.entriesFrom(map, Nat.compare, 1)) == [(1, "One"), (3, "Three")]; - /// assert Iter.toArray(Map.entriesFrom(map, Nat.compare, 2)) == [(3, "Three")]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func entriesFrom( - self : Map, - compare : (implicit : (K, K) -> Order.Order), - key : K - ) : Types.Iter<(K, V)> { - switch (self.root) { - case (#leaf(leafNode)) leafEntriesFrom(leafNode, compare, key); - case (#internal(internalNode)) internalEntriesFrom(internalNode, compare, key) - } - }; - - /// Returns an iterator over the key-value pairs in the map, - /// traversing the entries in the descending order of the keys. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.reverseEntries(map)) == [(2, "Two"), (1, "One"), (0, "Zero")]; - /// var sum = 0; - /// var text = ""; - /// for ((k, v) in Map.reverseEntries(map)) { sum += k; text #= v }; - /// assert sum == 3; - /// assert text == "TwoOneZero" - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseEntries(self : Map) : Types.Iter<(K, V)> { - switch (self.root) { - case (#leaf(leafNode)) reverseLeafEntries(leafNode); - case (#internal(internalNode)) reverseInternalEntries(internalNode) - } - }; - - /// Returns an iterator over the key-value pairs in the map, - /// starting from a given key in descending order. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (1, "One"), (3, "Three")].values(), Nat.compare); - /// assert Iter.toArray(Map.reverseEntriesFrom(map, Nat.compare, 0)) == [(0, "Zero")]; - /// assert Iter.toArray(Map.reverseEntriesFrom(map, Nat.compare, 2)) == [(1, "One"), (0, "Zero")]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseEntriesFrom( - self : Map, - compare : (implicit : (K, K) -> Order.Order), - key : K - ) : Types.Iter<(K, V)> { - switch (self.root) { - case (#leaf(leafNode)) reverseLeafEntriesFrom(leafNode, compare, key); - case (#internal(internalNode)) reverseInternalEntriesFrom(internalNode, compare, key) - } - }; - - /// Returns an iterator over the keys in the map, - /// traversing all keys in ascending order. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.keys(map)) == [0, 1, 2]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func keys(self : Map) : Types.Iter { - object { - let iterator = entries(self); - - public func next() : ?K { - switch (iterator.next()) { - case null null; - case (?(key, _)) ?key - } - } - } - }; - - /// Returns an iterator over the values in the map, - /// traversing the values in the ascending order of the keys to which they are associated. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.values(map)) == ["Zero", "One", "Two"]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func values(self : Map) : Types.Iter { - object { - let iterator = entries(self); - - public func next() : ?V { - switch (iterator.next()) { - case null null; - case (?(_, value)) ?value - } - } - } - }; - - /// Create a mutable key-value map with the entries obtained from an iterator. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = - /// Iter.fromArray([(0, "Zero"), (2, "Two"), (1, "One")]); - /// - /// let map = Map.fromIter(iter, Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func fromIter(iter : Types.Iter<(K, V)>, compare : (implicit : (K, K) -> Order.Order)) : Map { - let map = empty(); - for ((key, value) in iter) { - add(map, compare, key, value) - }; - map - }; - - /// Converts an iterator of entries into a Map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = - /// Iter.fromArray([(0, "Zero"), (2, "Two"), (1, "One")]); - /// - /// let map = iter.toMap(Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func toMap(self : Types.Iter<(K, V)>, compare : (implicit : (K, K) -> Order.Order)) : Map { - fromIter(self, compare) - }; - - public func fromArray(array : [(K, V)], compare : (implicit : (K, K) -> Order.Order)) : Map { - fromIter(array.values(), compare) - }; - - public func fromVarArray(array : [var (K, V)], compare : (implicit : (K, K) -> Order.Order)) : Map { - fromIter(array.values(), compare) - }; - - /// Apply an operation on each key-value pair contained in the map. - /// The operation is applied in ascending order of the keys. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// var sum = 0; - /// var text = ""; - /// Map.forEach(map, func (key, value) { - /// sum += key; - /// text #= value; - /// }); - /// assert sum == 3; - /// assert text == "ZeroOneTwo"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func forEach(self : Map, operation : (K, V) -> ()) { - for (entry in entries(self)) { - operation(entry) - } - }; - - /// Filter entries in a new map. - /// Create a copy of the mutable map that only contains the key-value pairs - /// that fulfil the criterion function. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numberNames = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// let evenNames = Map.filter(numberNames, Nat.compare, func (key, value) { - /// key % 2 == 0 - /// }); - /// - /// assert Iter.toArray(Map.entries(evenNames)) == [(0, "Zero"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func filter(self : Map, compare : (implicit : (K, K) -> Order.Order), criterion : (K, V) -> Bool) : Map { - let result = empty(); - for ((key, value) in entries(self)) { - if (criterion(key, value)) { - add(result, compare, key, value) - } - }; - result - }; - - /// Project all values of the map in a new map. - /// Apply a mapping function to the values of each entry in the map and - /// collect the mapped entries in a new mutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func f(key : Nat, _val : Text) : Nat = key * 2; - /// - /// let resMap = Map.map(map, f); - /// - /// assert Iter.toArray(Map.entries(resMap)) == [(0, 0), (1, 2), (2, 4)]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func map(self : Map, project : (K, V1) -> V2) : Map { - { - var root = mapNode(self.root, project); - var size = self.size - } - }; - - /// Iterate all entries in ascending order of the keys, - /// and accumulate the entries by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func folder(accum : (Nat, Text), key : Nat, val : Text) : ((Nat, Text)) - /// = (key + accum.0, accum.1 # val); - /// - /// assert Map.foldLeft(map, (0, ""), folder) == (3, "ZeroOneTwo"); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func foldLeft( - self : Map, - base : A, - combine : (A, K, V) -> A - ) : A { - var accumulator = base; - for ((key, value) in entries(self)) { - accumulator := combine(accumulator, key, value) - }; - accumulator - }; - - /// Iterate all entries in descending order of the keys, - /// and accumulate the entries by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func folder(key : Nat, val : Text, accum : (Nat, Text)) : ((Nat, Text)) - /// = (key + accum.0, accum.1 # val); - /// - /// assert Map.foldRight(map, (0, ""), folder) == (3, "TwoOneZero"); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func foldRight( - self : Map, - base : A, - combine : (K, V, A) -> A - ) : A { - var accumulator = base; - for ((key, value) in reverseEntries(self)) { - accumulator := combine(key, value, accumulator) - }; - accumulator - }; - - /// Check whether all entries in the map fulfil a predicate function, i.e. - /// the predicate function returns `true` for all entries in the map. - /// Returns `true` for an empty map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "0"), (2, "2"), (1, "1")].values(), Nat.compare); - /// - /// assert Map.all(map, func (k, v) = v == Nat.toText(k)); - /// assert not Map.all(map, func (k, v) = k < 2); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func all(self : Map, predicate : (K, V) -> Bool) : Bool { - //TODO: optimize - for (entry in entries(self)) { - if (not predicate(entry)) { - return false - } - }; - true - }; - - /// Test if any key-value pair in `map` satisfies the given predicate `pred`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "0"), (2, "2"), (1, "1")].values(), Nat.compare); - /// - /// assert Map.any(map, func (k, v) = (k >= 0)); - /// assert not Map.any(map, func (k, v) = (k >= 3)); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func any(self : Map, predicate : (K, V) -> Bool) : Bool { - //TODO: optimize - for (entry in entries(self)) { - if (predicate(entry)) { - return true - } - }; - false - }; - - /// Filter all entries in the map by also applying a projection to the value. - /// Apply a mapping function `project` to all entries in the map and collect all - /// entries, for which the function returns a non-null new value. Collect all - /// non-discarded entries with the key and new value in a new mutable map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func f(key : Nat, val : Text) : ?Text { - /// if(key == 0) {null} - /// else { ?("Twenty " # val)} - /// }; - /// - /// let newMap = Map.filterMap(map, Nat.compare, f); - /// - /// assert Iter.toArray(Map.entries(newMap)) == [(1, "Twenty One"), (2, "Twenty Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func filterMap(self : Map, compare : (implicit : (K, K) -> Order.Order), project : (K, V1) -> ?V2) : Map { - let result = empty(); - for ((key, value1) in entries(self)) { - switch (project(key, value1)) { - case null {}; - case (?value2) add(result, compare, key, value2) - } - }; - result - }; - - /// Internal sanity check function. - /// Can be used to check that key/value pairs have been inserted with a consistent key comparison function. - /// Traps if the internal map structure is invalid. - /// @deprecated M0235 - public func assertValid(self : Map, compare : (implicit : (K, K) -> Order.Order)) { - func checkIteration(iterator : Types.Iter<(K, V)>, order : Order.Order) { - switch (iterator.next()) { - case null {}; - case (?first) { - var previous = first; - loop { - switch (iterator.next()) { - case null return; - case (?next) { - if (compare(previous.0, next.0) != order) { - Runtime.trap("Invalid order") - }; - previous := next - } - } - } - } - } - }; - checkIteration(entries(self), #less); - checkIteration(reverseEntries(self), #greater) - }; - - /// Generate a textual representation of all the entries in the map. - /// Primarily to be used for testing and debugging. - /// The keys and values are formatted according to `keyFormat` and `valueFormat`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// assert Map.toText(map, Nat.toText, func t { t }) == "Map{(0, Zero), (1, One), (2, Two)}"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that `keyFormat` and `valueFormat` have runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func toText(self : Map, keyFormat : (implicit : (toText : K -> Text)), valueFormat : (implicit : (toText : V -> Text))) : Text { - var text = "Map{"; - var sep = ""; - for ((key, value) in entries(self)) { - text #= sep # "(" # keyFormat(key) # ", " # valueFormat(value) # ")"; - sep := ", " - }; - text # "}" - }; - - /// Compare two maps by primarily comparing keys and secondarily values. - /// Both maps must have been created by the same key comparison function. - /// The two maps are iterated by the ascending order of their creation and - /// order is determined by the following rules: - /// Less: - /// `map1` is less than `map2` if: - /// * the pairwise iteration hits a entry pair `entry1` and `entry2` where - /// `entry1` is less than `entry2` and all preceding entry pairs are equal, or, - /// * `map1` is a strict prefix of `map2`, i.e. `map2` has more entries than `map1` - /// and all entries of `map1` occur at the beginning of iteration `map2`. - /// `entry1` is less than `entry2` if: - /// * the key of `entry1` is less than the key of `entry2`, or - /// * `entry1` and `entry2` have equal keys and the value of `entry1` is less than - /// the value of `entry2`. - /// Equal: - /// `map1` and `map2` have same series of equal entries by pairwise iteration. - /// Greater: - /// `map1` is neither less nor equal `map2`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let map1 = Map.fromIter([(0, "Zero"), (1, "One")].values(), Nat.compare); - /// let map2 = Map.fromIter([(0, "Zero"), (2, "Two")].values(), Nat.compare); - /// - /// assert Map.compare(map1, map2, Nat.compare, Text.compare) == #less; - /// assert Map.compare(map1, map1, Nat.compare, Text.compare) == #equal; - /// assert Map.compare(map2, map1, Nat.compare, Text.compare) == #greater - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that `compareKey` and `compareValue` have runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func compare(self : Map, other : Map, compareKey : (implicit : (compare : (K, K) -> Order.Order)), compareValue : (implicit : (compare : (V, V) -> Order.Order))) : Order.Order { - let iterator1 = entries(self); - let iterator2 = entries(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?(key1, value1), ?(key2, value2)) { - let keyComparison = compareKey(key1, key2); - if (keyComparison != #equal) { - return keyComparison - }; - let valueComparison = compareValue(value1, value2); - if (valueComparison != #equal) { - return valueComparison - } - } - } - } - }; - - func leafEntries({ data } : Leaf) : Types.Iter<(K, V)> { - var i : Nat = 0; - object { - public func next() : ?(K, V) { - if (i >= data.count) { - null - } else { - let res = data.kvs[i]; - i += 1; - res - } - } - } - }; - - func leafEntriesFrom({ data } : Leaf, compare : (K, K) -> Order.Order, key : K) : Types.Iter<(K, V)> { - var i = switch (BinarySearch.binarySearchNode(data.kvs, compare, key, data.count)) { - case (#keyFound(i)) i; - case (#notFound(i)) i - }; - object { - public func next() : ?(K, V) { - if (i >= data.count) { - null - } else { - let res = data.kvs[i]; - i += 1; - res - } - } - } - }; - - func reverseLeafEntries({ data } : Leaf) : Types.Iter<(K, V)> { - var i : Nat = data.count; - object { - public func next() : ?(K, V) { - if (i == 0) { - null - } else { - let res = data.kvs[i - 1]; - i -= 1; - res - } - } - } - }; - - func reverseLeafEntriesFrom({ data } : Leaf, compare : (K, K) -> Order.Order, key : K) : Types.Iter<(K, V)> { - var i = switch (BinarySearch.binarySearchNode(data.kvs, compare, key, data.count)) { - case (#keyFound(i)) i + 1; // +1 to include this key - case (#notFound(i)) i // i is the index of the first key greater than the search key, or count if all keys are less than the search key - }; - object { - public func next() : ?(K, V) { - if (i == 0) { - null - } else { - let res = data.kvs[i - 1]; - i -= 1; - res - } - } - } - }; - - // Cursor type that keeps track of the current node and the current key-value index in the node - type NodeCursor = { node : Node; kvIndex : Nat }; - - func internalEntries(internal : Internal) : Types.Iter<(K, V)> { - // The nodeCursorStack keeps track of the current node and the current key-value index in the node - // We use a stack here to push to/pop off the next node cursor to visit - let nodeCursorStack = initializeForwardNodeCursorStack(internal); - internalEntriesFromStack(nodeCursorStack) - }; - - func internalEntriesFrom(internal : Internal, compare : (K, K) -> Order.Order, key : K) : Types.Iter<(K, V)> { - let nodeCursorStack = initializeForwardNodeCursorStackFrom(internal, compare, key); - internalEntriesFromStack(nodeCursorStack) - }; - - func internalEntriesFromStack(nodeCursorStack : Stack.Stack>) : Types.Iter<(K, V)> { - object { - public func next() : ?(K, V) { - // pop the next node cursor off the stack - var nodeCursor = Stack.pop(nodeCursorStack); - switch (nodeCursor) { - case null { return null }; - case (?{ node; kvIndex }) { - switch (node) { - // if a leaf node, iterate through the leaf node's next key-value pair - case (#leaf(leafNode)) { - let lastKV = leafNode.data.count - 1 : Nat; - if (kvIndex > lastKV) { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalEntries(), leaf kvIndex out of bounds") - }; - - let currentKV = switch (leafNode.data.kvs[kvIndex]) { - case (?kv) { kv }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Map.internalEntries(), null key-value pair found in leaf node." - # "leafNode.data.count=" # debug_show (leafNode.data.count) # ", kvIndex=" # debug_show (kvIndex) - ) - } - }; - // if not at the last key-value pair, push the next key-value index of the leaf onto the stack and return the current key-value pair - if (kvIndex < lastKV) { - Stack.push( - nodeCursorStack, - { - node = #leaf(leafNode); - kvIndex = kvIndex + 1 : Nat - } - ) - }; - - // return the current key-value pair - ?currentKV - }; - // if an internal node - case (#internal(internalNode)) { - let lastKV = internalNode.data.count - 1 : Nat; - // Developer facing message in case of a bug - if (kvIndex > lastKV) { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalEntries(), internal kvIndex out of bounds") - }; - - let currentKV = switch (internalNode.data.kvs[kvIndex]) { - case (?kv) { kv }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Map.internalEntries(), null key-value pair found in internal node. " # - "internal.data.count=" # debug_show (internalNode.data.count) # ", kvIndex=" # debug_show (kvIndex) - ) - } - }; - - let nextCursor = { - node = #internal(internalNode); - kvIndex = kvIndex + 1 : Nat - }; - // if not the last key-value pair, push the next key-value index of the internal node onto the stack - if (kvIndex < lastKV) { - Stack.push(nodeCursorStack, nextCursor) - }; - // traverse the next child's min subtree and push the resulting node cursors onto the stack - // then return the current key-value pair of the internal node - traverseMinSubtreeIter(nodeCursorStack, nextCursor); - ?currentKV - } - } - } - } - } - } - }; - - func reverseInternalEntries(internal : Internal) : Types.Iter<(K, V)> { - // The nodeCursorStack keeps track of the current node and the current key-value index in the node - // We use a stack here to push to/pop off the next node cursor to visit - let nodeCursorStack = initializeReverseNodeCursorStack(internal); - reverseInternalEntriesFromStack(nodeCursorStack) - }; - - func reverseInternalEntriesFrom(internal : Internal, compare : (K, K) -> Order.Order, key : K) : Types.Iter<(K, V)> { - let nodeCursorStack = initializeReverseNodeCursorStackFrom(internal, compare, key); - reverseInternalEntriesFromStack(nodeCursorStack) - }; - - func reverseInternalEntriesFromStack(nodeCursorStack : Stack.Stack>) : Types.Iter<(K, V)> { - object { - public func next() : ?(K, V) { - // pop the next node cursor off the stack - var nodeCursor = Stack.pop(nodeCursorStack); - switch (nodeCursor) { - case null { return null }; - case (?{ node; kvIndex }) { - let firstKV = 0 : Nat; - assert (kvIndex > firstKV); - switch (node) { - // if a leaf node, reverse iterate through the leaf node's next key-value pair - case (#leaf(leafNode)) { - let currentKV = switch (leafNode.data.kvs[kvIndex - 1]) { - case (?kv) { kv }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Map.reverseInternalEntries(), null key-value pair found in leaf node." - # "leafNode.data.count=" # debug_show (leafNode.data.count) # ", kvIndex=" # debug_show (kvIndex) - ) - } - }; - // if not at the last key-value pair, push the previous key-value index of the leaf onto the stack and return the current key-value pair - if (kvIndex - 1 : Nat > firstKV) { - Stack.push( - nodeCursorStack, - { - node = #leaf(leafNode); - kvIndex = kvIndex - 1 : Nat - } - ) - }; - - // return the current key-value pair - ?currentKV - }; - // if an internal node - case (#internal(internalNode)) { - let currentKV = switch (internalNode.data.kvs[kvIndex - 1]) { - case (?kv) { kv }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Map.reverseInternalEntries(), null key-value pair found in internal node. " # - "internal.data.count=" # debug_show (internalNode.data.count) # ", kvIndex=" # debug_show (kvIndex) - ) - } - }; - - let previousCursor = { - node = #internal(internalNode); - kvIndex = kvIndex - 1 : Nat - }; - // if not the first key-value pair, push the previous key-value index of the internal node onto the stack - if (kvIndex - 1 : Nat > firstKV) { - Stack.push(nodeCursorStack, previousCursor) - }; - // traverse the previous child's max subtree and push the resulting node cursors onto the stack - // then return the current key-value pair of the internal node - traverseMaxSubtreeIter(nodeCursorStack, previousCursor); - ?currentKV - } - } - } - } - } - } - }; - - func initializeForwardNodeCursorStack(internal : Internal) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - kvIndex = 0 - }; - - // push the initial cursor to the stack - Stack.push(nodeCursorStack, nodeCursor); - // then traverse left - traverseMinSubtreeIter(nodeCursorStack, nodeCursor); - nodeCursorStack - }; - - func initializeForwardNodeCursorStackFrom(internal : Internal, compare : (K, K) -> Order.Order, key : K) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - kvIndex = 0 - }; - - traverseMinSubtreeIterFrom(nodeCursorStack, nodeCursor, compare, key); - nodeCursorStack - }; - - func initializeReverseNodeCursorStack(internal : Internal) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - kvIndex = internal.data.count - }; - - // push the initial cursor to the stack - Stack.push(nodeCursorStack, nodeCursor); - // then traverse left - traverseMaxSubtreeIter(nodeCursorStack, nodeCursor); - nodeCursorStack - }; - - func initializeReverseNodeCursorStackFrom(internal : Internal, compare : (K, K) -> Order.Order, key : K) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - kvIndex = internal.data.count - }; - - traverseMaxSubtreeIterFrom(nodeCursorStack, nodeCursor, compare, key); - nodeCursorStack - }; - - // traverse the min subtree of the current node cursor, passing each new element to the node cursor stack - func traverseMinSubtreeIter(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor) { - var currentNode = nodeCursor.node; - var childIndex = nodeCursor.kvIndex; - - label l loop { - switch (currentNode) { - // If currentNode is leaf, have hit the minimum element of the subtree and already pushed it's cursor to the stack - // so can return - case (#leaf(_)) { - return - }; - // If currentNode is internal, add it's left most child to the stack and continue traversing - case (#internal(internalNode)) { - switch (internalNode.children[childIndex]) { - // Push the next min (left most) child node to the stack - case (?childNode) { - childIndex := 0; - currentNode := childNode; - Stack.push( - nodeCursorStack, - { - node = currentNode; - kvIndex = childIndex - } - ) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.traverseMinSubtreeIter(), null child node error") - } - } - } - } - } - }; - - func traverseMinSubtreeIterFrom(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor, compare : (K, K) -> Order.Order, key : K) { - var currentNode = nodeCursor.node; - - label l loop { - let (node, childrenOption) = switch (currentNode) { - case (#leaf(leafNode)) (leafNode, null); - case (#internal(internalNode)) (internalNode, ?internalNode.children) - }; - let (i, isFound) = switch (NodeUtil.getKeyIndex(node.data, compare, key)) { - case (#keyFound(i)) (i, true); - case (#notFound(i)) (i, false) - }; - if (i < node.data.count) { - Stack.push( - nodeCursorStack, - { - node = currentNode; - kvIndex = i // greater entries to traverse - } - ) - }; - if isFound return; - let ?children = childrenOption else return; - let ?childNode = children[i] else Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.traverseMinSubtreeIterFrom(), null child node error"); - currentNode := childNode - } - }; - - // traverse the max subtree of the current node cursor, passing each new element to the node cursor stack - func traverseMaxSubtreeIter(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor) { - var currentNode = nodeCursor.node; - var childIndex = nodeCursor.kvIndex; - - label l loop { - switch (currentNode) { - // If currentNode is leaf, have hit the maximum element of the subtree and already pushed it's cursor to the stack - // so can return - case (#leaf(_)) { - return - }; - // If currentNode is internal, add it's right most child to the stack and continue traversing - case (#internal(internalNode)) { - assert (childIndex <= internalNode.data.count); // children are one more than data entries - switch (internalNode.children[childIndex]) { - // Push the next max (right most) child node to the stack - case (?childNode) { - childIndex := switch (childNode) { - case (#internal(internalNode)) internalNode.data.count; - case (#leaf(leafNode)) leafNode.data.count - }; - currentNode := childNode; - Stack.push( - nodeCursorStack, - { - node = currentNode; - kvIndex = childIndex - } - ) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.traverseMaxSubtreeIter(), null child node error") - } - } - } - } - } - }; - - func traverseMaxSubtreeIterFrom(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor, compare : (K, K) -> Order.Order, key : K) { - var currentNode = nodeCursor.node; - - label l loop { - let (node, childrenOption) = switch (currentNode) { - case (#leaf(leafNode)) (leafNode, null); - case (#internal(internalNode)) (internalNode, ?internalNode.children) - }; - let (i, isFound) = switch (NodeUtil.getKeyIndex(node.data, compare, key)) { - case (#keyFound(i)) (i + 1, true); // +1 to include this key - case (#notFound(i)) (i, false) // i is the index of the first key less than the search key, or 0 if all keys are greater than the search key - }; - if (i > 0) { - Stack.push( - nodeCursorStack, - { - node = currentNode; - kvIndex = i - } - ) - }; - if isFound return; - let ?children = childrenOption else return; - let ?childNode = children[i] else Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.traverseMaxSubtreeIterFrom(), null child node error"); - currentNode := childNode - } - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateInternalDeleteResult = { - // element was deleted or not found, returning the old value (?value or null) - #delete : ?V; - // deleted an element, but was unable to successfully borrow and rebalance at the previous level without merging children - // the internalChild is the merged child that needs to be rebalanced at the next level up in the BTree - #mergeChild : { - internalChild : Internal; - deletedValue : ?V - } - }; - - func internalDeleteHelper(internalNode : Internal, order : Nat, compare : (K, K) -> Order.Order, deleteKey : K, skipNode : Bool) : IntermediateInternalDeleteResult { - let minKeys = NodeUtil.minKeysFromOrder(order); - let keyIndex = NodeUtil.getKeyIndex(internalNode.data, compare, deleteKey); - - // match on both the result of the node binary search, and if this node level should be skipped even if the key is found (internal kv replacement case) - switch (keyIndex, skipNode) { - // if key is found in the internal node - case (#keyFound(deleteIndex), false) { - let deletedValue = switch (internalNode.data.kvs[deleteIndex]) { - case (?kv) { ?kv.1 }; - case null { assert false; null } - }; - // TODO: (optimization) replace with deletion in one step without having to retrieve the maxKey first - let replaceKV = NodeUtil.getMaxKeyValue(internalNode.children[deleteIndex]); - internalNode.data.kvs[deleteIndex] := ?replaceKV; - switch (internalDeleteHelper(internalNode, order, compare, replaceKV.0, true)) { - case (#delete(_)) { #delete(deletedValue) }; - case (#mergeChild({ internalChild })) { - #mergeChild({ internalChild; deletedValue }) - } - } - }; - // if key is not found in the internal node OR the key is found, but skipping this node (because deleting the in order precessor i.e. replacement kv) - // in both cases need to descend and traverse to find the kv to delete - case ((#keyFound(_), true) or (#notFound(_), _)) { - let childIndex = switch (keyIndex) { - case (#keyFound(replacedSkipKeyIndex)) { replacedSkipKeyIndex }; - case (#notFound(childIndex)) { childIndex } - }; - let child = switch (internalNode.children[childIndex]) { - case (?c) { c }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalDeleteHelper, child index of #keyFound or #notfound is null") - } - }; - switch (child) { - // if child is internal - case (#internal(internalChild)) { - switch (internalDeleteHelper(internalChild, order, compare, deleteKey, false), childIndex == 0) { - // if value was successfully deleted and no additional tree re-balancing is needed, return the deleted value - case (#delete(v), _) { #delete(v) }; - // if internalChild needs rebalancing and pulling child is left most - case (#mergeChild({ internalChild; deletedValue }), true) { - // try to pull left-most key and child from right sibling - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex + 1, #successor)) { - // if can pull up sibling kv and child - case (#borrowed({ deletedSiblingKVPair; child })) { - NodeUtil.rotateBorrowedKVsAndChildFromSibling( - internalNode, - childIndex, - deletedSiblingKVPair, - child, - internalChild, - #right - ); - #delete(deletedValue) - }; - // unable to pull from sibling, need to merge with right sibling and push down parent - case (#notEnoughKeys(sibling)) { - // get the parent kv that will be pushed down the the child - let kvPairToBePushedToChild = ?BTreeHelper.deleteAndShift(internalNode.data.kvs, 0); - internalNode.data.count -= 1; - // merge the children and push down the parent - let newChild = NodeUtil.mergeChildrenAndPushDownParent(internalChild, kvPairToBePushedToChild, sibling); - // update children of the parent - internalNode.children[0] := ?#internal(newChild); - ignore ?BTreeHelper.deleteAndShift(internalNode.children, 1); - - if (internalNode.data.count < minKeys) { - #mergeChild({ internalChild = internalNode; deletedValue }) - } else { - #delete(deletedValue) - } - } - } - }; - // if internalChild needs rebalancing and pulling child is > 0, so a left sibling exists - case (#mergeChild({ internalChild; deletedValue }), false) { - // try to pull right-most key and its child directly from left sibling - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex - 1 : Nat, #predecessor)) { - case (#borrowed({ deletedSiblingKVPair; child })) { - NodeUtil.rotateBorrowedKVsAndChildFromSibling( - internalNode, - childIndex - 1 : Nat, - deletedSiblingKVPair, - child, - internalChild, - #left - ); - #delete(deletedValue) - }; - // unable to pull from left sibling - case (#notEnoughKeys(leftSibling)) { - // if child is not last index, try to pull from the right child - if (childIndex < internalNode.data.count) { - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex, #successor)) { - // if can pull up sibling kv and child - case (#borrowed({ deletedSiblingKVPair; child })) { - NodeUtil.rotateBorrowedKVsAndChildFromSibling( - internalNode, - childIndex, - deletedSiblingKVPair, - child, - internalChild, - #right - ); - return #delete(deletedValue) - }; - // if cannot borrow, from left or right, merge (see below) - case _ {} - } - }; - - // get the parent kv that will be pushed down the the child - let kvPairToBePushedToChild = ?BTreeHelper.deleteAndShift(internalNode.data.kvs, childIndex - 1 : Nat); - internalNode.data.count -= 1; - // merge it the children and push down the parent - let newChild = NodeUtil.mergeChildrenAndPushDownParent(leftSibling, kvPairToBePushedToChild, internalChild); - - // update children of the parent - internalNode.children[childIndex - 1] := ?#internal(newChild); - ignore ?BTreeHelper.deleteAndShift(internalNode.children, childIndex); - - if (internalNode.data.count < minKeys) { - #mergeChild({ internalChild = internalNode; deletedValue }) - } else { - #delete(deletedValue) - } - } - } - } - } - }; - // if child is leaf - case (#leaf(leafChild)) { - switch (leafDeleteHelper(leafChild, order, compare, deleteKey), childIndex == 0) { - case (#delete(value), _) { #delete(value) }; - // if delete child is left most, try to borrow from right child - case (#mergeLeafData({ leafDeleteIndex }), true) { - switch (NodeUtil.borrowFromRightLeafChild(internalNode.children, childIndex)) { - case (?borrowedKVPair) { - let kvPairToBePushedToChild = internalNode.data.kvs[childIndex]; - internalNode.data.kvs[childIndex] := ?borrowedKVPair; - - let deletedKV = BTreeHelper.insertAtPostionAndDeleteAtPosition<(K, V)>(leafChild.data.kvs, kvPairToBePushedToChild, leafChild.data.count - 1, leafDeleteIndex); - #delete(?deletedKV.1) - }; - - case null { - // can't borrow from right child, delete from leaf and merge with right child and parent kv, then push down into new leaf - let rightChild = switch (internalNode.children[childIndex + 1]) { - case (?#leaf(rc)) { rc }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalDeleteHelper, if trying to borrow from right leaf child is null, rightChild index cannot be null or internal") - } - }; - let (mergedLeaf, deletedKV) = mergeParentWithLeftRightChildLeafNodesAndDelete( - internalNode.data.kvs[childIndex], - leafChild, - rightChild, - leafDeleteIndex, - #left - ); - // delete the left most internal node kv, since was merging from a deletion in left most child (0) and the parent kv was pushed into the mergedLeaf - ignore BTreeHelper.deleteAndShift<(K, V)>(internalNode.data.kvs, 0); - // update internal node children - BTreeHelper.replaceTwoWithElementAndShift>(internalNode.children, #leaf(mergedLeaf), 0); - internalNode.data.count -= 1; - - if (internalNode.data.count < minKeys) { - #mergeChild({ - internalChild = internalNode; - deletedValue = ?deletedKV.1 - }) - } else { - #delete(?deletedKV.1) - } - - } - } - }; - // if delete child is middle or right most, try to borrow from left child - case (#mergeLeafData({ leafDeleteIndex }), false) { - // if delete child is right most, try to borrow from left child - switch (NodeUtil.borrowFromLeftLeafChild(internalNode.children, childIndex)) { - case (?borrowedKVPair) { - let kvPairToBePushedToChild = internalNode.data.kvs[childIndex - 1]; - internalNode.data.kvs[childIndex - 1] := ?borrowedKVPair; - let kvDelete = BTreeHelper.insertAtPostionAndDeleteAtPosition<(K, V)>(leafChild.data.kvs, kvPairToBePushedToChild, 0, leafDeleteIndex); - #delete(?kvDelete.1) - }; - case null { - // if delete child is in the middle, try to borrow from right child - if (childIndex < internalNode.data.count) { - // try to borrow from right - switch (NodeUtil.borrowFromRightLeafChild(internalNode.children, childIndex)) { - case (?borrowedKVPair) { - let kvPairToBePushedToChild = internalNode.data.kvs[childIndex]; - internalNode.data.kvs[childIndex] := ?borrowedKVPair; - // insert the successor at the very last element - let kvDelete = BTreeHelper.insertAtPostionAndDeleteAtPosition<(K, V)>(leafChild.data.kvs, kvPairToBePushedToChild, leafChild.data.count - 1, leafDeleteIndex); - return #delete(?kvDelete.1) - }; - // if cannot borrow, from left or right, merge (see below) - case _ {} - } - }; - - // can't borrow from left child, delete from leaf and merge with left child and parent kv, then push down into new leaf - let leftChild = switch (internalNode.children[childIndex - 1]) { - case (?#leaf(lc)) { lc }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalDeleteHelper, if trying to borrow from left leaf child is null, then left child index must not be null or internal") - } - }; - let (mergedLeaf, deletedKV) = mergeParentWithLeftRightChildLeafNodesAndDelete( - internalNode.data.kvs[childIndex - 1], - leftChild, - leafChild, - leafDeleteIndex, - #right - ); - // delete the right most internal node kv, since was merging from a deletion in the right most child and the parent kv was pushed into the mergedLeaf - ignore BTreeHelper.deleteAndShift<(K, V)>(internalNode.data.kvs, childIndex - 1); - // update internal node children - BTreeHelper.replaceTwoWithElementAndShift>(internalNode.children, #leaf(mergedLeaf), childIndex - 1); - internalNode.data.count -= 1; - - if (internalNode.data.count < minKeys) { - #mergeChild({ - internalChild = internalNode; - deletedValue = ?deletedKV.1 - }) - } else { - #delete(?deletedKV.1) - } - } - } - } - } - } - } - } - } - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateLeafDeleteResult = { - // element was deleted or not found, returning the old value (?value or null) - #delete : ?V; - // leaf had the minimum number of keys when deleting, so returns the leaf node's data and the index of the key that will be deleted - #mergeLeafData : { - data : Data; - leafDeleteIndex : Nat - } - }; - - func leafDeleteHelper(leafNode : Leaf, order : Nat, compare : (K, K) -> Order.Order, deleteKey : K) : IntermediateLeafDeleteResult { - let minKeys = NodeUtil.minKeysFromOrder(order); - - switch (NodeUtil.getKeyIndex(leafNode.data, compare, deleteKey)) { - case (#keyFound(deleteIndex)) { - if (leafNode.data.count > minKeys) { - leafNode.data.count -= 1; - #delete(?BTreeHelper.deleteAndShift<(K, V)>(leafNode.data.kvs, deleteIndex).1) - } else { - #mergeLeafData({ - data = leafNode.data; - leafDeleteIndex = deleteIndex - }) - } - }; - case (#notFound(_)) { - #delete(null) - } - } - }; - - // get helper if internal node - func getFromInternal(internalNode : Internal, compare : (K, K) -> Order.Order, key : K) : ?V { - switch (NodeUtil.getKeyIndex(internalNode.data, compare, key)) { - case (#keyFound(index)) { - getExistingValueFromIndex(internalNode.data, index) - }; - case (#notFound(index)) { - switch (internalNode.children[index]) { - // expects the child to be there, otherwise there's a bug in binary search or the tree is invalid - case null { Runtime.trap("Internal bug: Map.getFromInternal") }; - case (?#leaf(leafNode)) { getFromLeaf(leafNode, compare, key) }; - case (?#internal(internalNode)) { - getFromInternal(internalNode, compare, key) - } - } - } - } - }; - - // get function helper if leaf node - func getFromLeaf(leafNode : Leaf, compare : (K, K) -> Order.Order, key : K) : ?V { - switch (NodeUtil.getKeyIndex(leafNode.data, compare, key)) { - case (#keyFound(index)) { - getExistingValueFromIndex(leafNode.data, index) - }; - case _ null - } - }; - - // get function helper that retrieves an existing value in the case that the key is found - func getExistingValueFromIndex(data : Data, index : Nat) : ?V { - switch (data.kvs[index]) { - case null { null }; - case (?ov) { ?ov.1 } - } - }; - - // which child the deletionIndex is referring to - type DeletionSide = { #left; #right }; - - func mergeParentWithLeftRightChildLeafNodesAndDelete( - parentKV : ?(K, V), - leftChild : Leaf, - rightChild : Leaf, - deleteIndex : Nat, - deletionSide : DeletionSide - ) : (Leaf, (K, V)) { - let count = leftChild.data.count * 2; - let (kvs, deletedKV) = BTreeHelper.mergeParentWithChildrenAndDelete<(K, V)>( - parentKV, - leftChild.data.count, - leftChild.data.kvs, - rightChild.data.kvs, - deleteIndex, - deletionSide - ); - ( - { - data = { - kvs; - var count = count - } - }, - deletedKV - ) - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateInsertResult = { - // element was inserted or replaced, returning the old value (?value or null) - #insert : ?V; - // child was full when inserting, so returns the promoted kv pair and the split left and right child - #promote : { - kv : (K, V); - leftChild : Node; - rightChild : Node - } - }; - - // Helper for inserting into a leaf node - func leafInsertHelper(leafNode : Leaf, order : Nat, compare : (K, K) -> Order.Order, key : K, value : V) : (IntermediateInsertResult) { - // Perform binary search to see if the element exists in the node - switch (NodeUtil.getKeyIndex(leafNode.data, compare, key)) { - case (#keyFound(insertIndex)) { - let previous = leafNode.data.kvs[insertIndex]; - leafNode.data.kvs[insertIndex] := ?(key, value); - switch (previous) { - case (?ov) { #insert(?ov.1) }; - case null { assert false; #insert(null) }; // the binary search already found an element, so this case should never happen - } - }; - case (#notFound(insertIndex)) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - let maxKeys : Nat = order - 1; - // If the leaf is full, insert, split the node, and promote the middle element - if (leafNode.data.count >= maxKeys) { - let (leftKVs, promotedParentElement, rightKVs) = BTreeHelper.insertOneAtIndexAndSplitArray( - leafNode.data.kvs, - (key, value), - insertIndex - ); - - let leftCount = order / 2; - let rightCount : Nat = if (order % 2 == 0) { leftCount - 1 } else { - leftCount - }; - - ( - #promote({ - kv = promotedParentElement; - leftChild = createLeaf(leftKVs, leftCount); - rightChild = createLeaf(rightKVs, rightCount) - }) - ) - } - // Otherwise, insert at the specified index (shifting elements over if necessary) - else { - NodeUtil.insertAtIndexOfNonFullNodeData(leafNode.data, ?(key, value), insertIndex); - #insert(null) - } - } - } - }; - - // Helper for inserting into an internal node - func internalInsertHelper(internalNode : Internal, order : Nat, compare : (K, K) -> Order.Order, key : K, value : V) : IntermediateInsertResult { - switch (NodeUtil.getKeyIndex(internalNode.data, compare, key)) { - case (#keyFound(insertIndex)) { - let previous = internalNode.data.kvs[insertIndex]; - internalNode.data.kvs[insertIndex] := ?(key, value); - switch (previous) { - case (?ov) { #insert(?ov.1) }; - case null { assert false; #insert(null) }; // the binary search already found an element, so this case should never happen - } - }; - case (#notFound(insertIndex)) { - let insertResult = switch (internalNode.children[insertIndex]) { - case null { assert false; #insert(null) }; - case (?#leaf(leafNode)) { - leafInsertHelper(leafNode, order, compare, key, value) - }; - case (?#internal(internalChildNode)) { - internalInsertHelper(internalChildNode, order, compare, key, value) - } - }; - - switch (insertResult) { - case (#insert(ov)) { #insert(ov) }; - case (#promote({ kv; leftChild; rightChild })) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - let maxKeys : Nat = order - 1; - // if current internal node is full, need to split the internal node - if (internalNode.data.count >= maxKeys) { - // insert and split internal kvs, determine new promotion target kv - let (leftKVs, promotedParentElement, rightKVs) = BTreeHelper.insertOneAtIndexAndSplitArray( - internalNode.data.kvs, - (kv), - insertIndex - ); - - // calculate the element count in the left KVs and the element count in the right KVs - let leftCount = order / 2; - let rightCount : Nat = if (order % 2 == 0) { leftCount - 1 } else { - leftCount - }; - - // split internal children - let (leftChildren, rightChildren) = NodeUtil.splitChildrenInTwoWithRebalances( - internalNode.children, - insertIndex, - leftChild, - rightChild - ); - - // send the kv to be promoted, as well as the internal children left and right split - #promote({ - kv = promotedParentElement; - leftChild = #internal({ - data = { kvs = leftKVs; var count = leftCount }; - children = leftChildren - }); - rightChild = #internal({ - data = { kvs = rightKVs; var count = rightCount }; - children = rightChildren - }) - }) - } else { - // insert the new kvs into the internal node - NodeUtil.insertAtIndexOfNonFullNodeData(internalNode.data, ?kv, insertIndex); - // split and re-insert the single child that needs rebalancing - NodeUtil.insertRebalancedChild(internalNode.children, insertIndex, leftChild, rightChild); - #insert(null) - } - } - } - } - } - }; - - func createLeaf(kvs : [var ?(K, V)], count : Nat) : Node { - #leaf({ - data = { - kvs; - var count - } - }) - }; - - // Additional functionality compared to original source. - - func mapData(data : Data, project : (K, V1) -> V2) : Data { - { - kvs = VarArray.map( - data.kvs, - func entry { - switch entry { - case (?kv) ?(kv.0, project kv); - case null null - } - } - ); - var count = data.count - } - }; - - func mapNode(node : Node, project : (K, V1) -> V2) : Node { - switch node { - case (#leaf { data }) { - #leaf { data = mapData(data, project) } - }; - case (#internal { data; children }) { - let mappedData = mapData(data, project); - let mappedChildren = VarArray.map, ?Node>( - children, - func child { - switch child { - case null null; - case (?childNode) ?mapNode(childNode, project) - } - } - ); - # internal({ - data = mappedData; - children = mappedChildren - }) - } - } - }; - - func cloneNode(node : Node) : Node = mapNode(node, func(k, v) = v); - - module BinarySearch { - public type SearchResult = { - #keyFound : Nat; - #notFound : Nat - }; - - /// Searches an array for a specific key, returning the index it occurs at if #keyFound, or the child/insert index it may occur at - /// if #notFound. This is used when determining if a key exists in an internal or leaf node, where a key should be inserted in a - /// leaf node, or which child of an internal node a key could be in. - /// - /// Note: This function expects a mutable, nullable, array of keys in sorted order, where all nulls appear at the end of the array. - /// This function may trap if a null value appears before any values. It also expects a maxIndex, which is the right-most index (bound) - /// from which to begin the binary search (the left most bound is expected to be 0) - /// - /// Parameters: - /// - /// * array - the sorted array that the binary search is performed upon - /// * compare - the comparator used to perform the search - /// * searchKey - the key being compared against in the search - /// * maxIndex - the right-most index (bound) from which to begin the search - public func binarySearchNode(array : [var ?(K, V)], compare : (implicit : (K, K) -> Order.Order), searchKey : K, maxIndex : Nat) : SearchResult { - // TODO: get rid of this check? - // Trap if array is size 0 (should not happen) - if (array.size() == 0) { - assert false - }; - - // if all elements in the array are null (i.e. first element is null), return #notFound(0) - if (maxIndex == 0) { - return #notFound(0) - }; - - // Initialize search from first to last index - var left : Nat = 0; - var right = maxIndex; // maxIndex does not necessarily mean array.size() - 1 - // Search the array - while (left < right) { - let middle = (left + right) / 2; - switch (array[middle]) { - case null { assert false }; - case (?(key, _)) { - switch (compare(searchKey, key)) { - // If the element is present at the middle itself - case (#equal) { return #keyFound(middle) }; - // If element is greater than mid, it can only be present in left subarray - case (#greater) { left := middle + 1 }; - // If element is smaller than mid, it can only be present in right subarray - case (#less) { - right := if (middle == 0) { 0 } else { middle - 1 } - } - } - } - } - }; - - if (left == array.size()) { - return #notFound(left) - }; - - // left == right - switch (array[left]) { - // inserting at end of array - case null { #notFound(left) }; - case (?(key, _)) { - switch (compare(searchKey, key)) { - // if left is the key - case (#equal) { #keyFound(left) }; - // if the key is not found, return notFound and the insert location - case (#greater) { #notFound(left + 1) }; - case (#less) { #notFound(left) } - } - } - } - } - }; - - module NodeUtil { - /// Inserts element at the given index into a non-full leaf node - public func insertAtIndexOfNonFullNodeData(data : Data, kvPair : ?(K, V), insertIndex : Nat) { - let currentLastElementIndex : Nat = if (data.count == 0) { 0 } else { - data.count - 1 - }; - BTreeHelper.insertAtPosition<(K, V)>(data.kvs, kvPair, insertIndex, currentLastElementIndex); - - // increment the count of data in this node since just inserted an element - data.count += 1 - }; - - /// Inserts two rebalanced (split) child halves into a non-full array of children. - public func insertRebalancedChild(children : [var ?Node], rebalancedChildIndex : Nat, leftChildInsert : Node, rightChildInsert : Node) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - var j : Nat = children.size() - 2; - - // This is just a sanity check to ensure the children aren't already full (should split promote otherwise) - // TODO: Remove this check once confident - if (Option.isSome(children[j + 1])) { assert false }; - - // Iterate backwards over the array and shift each element over to the right by one until the rebalancedChildIndex is hit - while (j > rebalancedChildIndex) { - children[j + 1] := children[j]; - j -= 1 - }; - - // Insert both the left and right rebalanced children (replacing the pre-split child) - children[j] := ?leftChildInsert; - children[j + 1] := ?rightChildInsert - }; - - /// Used when splitting the children of an internal node - /// - /// Takes in the rebalanced child index, as well as both halves of the rebalanced child and splits the children, inserting the left and right child halves appropriately - /// - /// For more context, see the documentation for the splitArrayAndInsertTwo method in BTreeHelper.mo - public func splitChildrenInTwoWithRebalances( - children : [var ?Node], - rebalancedChildIndex : Nat, - leftChildInsert : Node, - rightChildInsert : Node - ) : ([var ?Node], [var ?Node]) { - BTreeHelper.splitArrayAndInsertTwo>(children, rebalancedChildIndex, leftChildInsert, rightChildInsert) - }; - - /// Helper used to get the key index of of a key within a node - /// - /// for more, see the BinarySearch.binarySearchNode() documentation - public func getKeyIndex(data : Data, compare : (K, K) -> Order.Order, key : K) : BinarySearch.SearchResult { - BinarySearch.binarySearchNode(data.kvs, compare, key, data.count) - }; - - // calculates a BTree Node's minimum allowed keys given the order of the BTree - public func minKeysFromOrder(order : Nat) : Nat { - if (order % 2 == 0) { order / 2 - 1 } else { order / 2 } - }; - - // Given a node, get the maximum key value (right most leaf kv) - public func getMaxKeyValue(node : ?Node) : (K, V) { - switch (node) { - case (?#leaf({ data })) { - switch (data.kvs[data.count - 1]) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.NodeUtil.getMaxKeyValue, data cannot have more elements than it's count") - }; - case (?kv) { kv } - } - }; - case (?#internal({ data; children })) { - getMaxKeyValue(children[data.count]) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.NodeUtil.getMaxKeyValue, the node provided cannot be null") - } - } - }; - - type InorderBorrowType = { - #predecessor; - #successor - }; - - // attempts to retrieve the in max key of the child leaf node directly to the left if the node will allow it - // returns the deleted max key if able to retrieve, null if not able - // - // mutates the predecessing node's keys - public func borrowFromLeftLeafChild(children : [var ?Node], ofChildIndex : Nat) : ?(K, V) { - let predecessorIndex : Nat = ofChildIndex - 1; - borrowFromLeafChild(children, predecessorIndex, #predecessor) - }; - - // attempts to retrieve the in max key of the child leaf node directly to the right if the node will allow it - // returns the deleted max key if able to retrieve, null if not able - // - // mutates the predecessing node's keys - public func borrowFromRightLeafChild(children : [var ?Node], ofChildIndex : Nat) : ?(K, V) { - borrowFromLeafChild(children, ofChildIndex + 1, #successor) - }; - - func borrowFromLeafChild(children : [var ?Node], borrowChildIndex : Nat, childSide : InorderBorrowType) : ?(K, V) { - let minKeys = minKeysFromOrder(children.size()); - - switch (children[borrowChildIndex]) { - case (?#leaf({ data })) { - if (data.count > minKeys) { - // able to borrow a key-value from this child, so decrement the count of kvs - data.count -= 1; // Since enforce order >= 4, there will always be at least 1 element per node - switch (childSide) { - case (#predecessor) { - let deletedKV = data.kvs[data.count]; - data.kvs[data.count] := null; - deletedKV - }; - case (#successor) { - ?BTreeHelper.deleteAndShift(data.kvs, 0) - } - } - } else { null } - }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.NodeUtil.borrowFromLeafChild, the node at the borrow child index cannot be null or internal") - } - } - }; - - type InternalBorrowResult = { - #borrowed : InternalBorrow; - #notEnoughKeys : Internal - }; - - type InternalBorrow = { - deletedSiblingKVPair : ?(K, V); - child : ?Node - }; - - // Attempts to borrow a KV and child from an internal sibling node - public func borrowFromInternalSibling(children : [var ?Node], borrowChildIndex : Nat, borrowType : InorderBorrowType) : InternalBorrowResult { - let minKeys = minKeysFromOrder(children.size()); - - switch (children[borrowChildIndex]) { - case (?#internal({ data; children })) { - if (data.count > minKeys) { - data.count -= 1; - switch (borrowType) { - case (#predecessor) { - let deletedSiblingKVPair = data.kvs[data.count]; - data.kvs[data.count] := null; - let child = children[data.count + 1]; - children[data.count + 1] := null; - #borrowed({ - deletedSiblingKVPair; - child - }) - }; - case (#successor) { - #borrowed({ - deletedSiblingKVPair = ?BTreeHelper.deleteAndShift(data.kvs, 0); - child = ?BTreeHelper.deleteAndShift(children, 0) - }) - } - } - } else { #notEnoughKeys({ data; children }) } - }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.NodeUtil.borrowFromInternalSibling from internal sibling, the child at the borrow index cannot be null or a leaf") - } - } - }; - - type SiblingSide = { #left; #right }; - - // Rotates the borrowed KV and child from sibling side of the internal node to the internal child recipient - public func rotateBorrowedKVsAndChildFromSibling( - internalNode : Internal, - parentRotateIndex : Nat, - borrowedSiblingKVPair : ?(K, V), - borrowedSiblingChild : ?Node, - internalChildRecipient : Internal, - siblingSide : SiblingSide - ) { - // if borrowing from the left, the rotated key and child will always be inserted first - // if borrowing from the right, the rotated key and child will always be inserted last - let (kvIndex, childIndex) = switch (siblingSide) { - case (#left) { (0, 0) }; - case (#right) { - (internalChildRecipient.data.count, internalChildRecipient.data.count + 1) - } - }; - - // get the parent kv that will be pushed down the the child - let kvPairToBePushedToChild = internalNode.data.kvs[parentRotateIndex]; - // replace the parent with the sibling kv - internalNode.data.kvs[parentRotateIndex] := borrowedSiblingKVPair; - // push the kv and child down into the internalChild - insertAtIndexOfNonFullNodeData(internalChildRecipient.data, kvPairToBePushedToChild, kvIndex); - - BTreeHelper.insertAtPosition>(internalChildRecipient.children, borrowedSiblingChild, childIndex, internalChildRecipient.data.count) - }; - - // Merges the kvs and children of two internal nodes, pushing the parent kv in between the right and left halves - public func mergeChildrenAndPushDownParent(leftChild : Internal, parentKV : ?(K, V), rightChild : Internal) : Internal { - { - data = mergeData(leftChild.data, parentKV, rightChild.data); - children = mergeChildren(leftChild.children, rightChild.children) - } - }; - - func mergeData(leftData : Data, parentKV : ?(K, V), rightData : Data) : Data { - assert leftData.count <= minKeysFromOrder(leftData.kvs.size() + 1); - assert rightData.count <= minKeysFromOrder(rightData.kvs.size() + 1); - - let mergedKVs = VarArray.repeat(null, leftData.kvs.size()); - var i = 0; - while (i < leftData.count) { - mergedKVs[i] := leftData.kvs[i]; - i += 1 - }; - - mergedKVs[i] := parentKV; - i += 1; - - var j = 0; - while (j < rightData.count) { - mergedKVs[i] := rightData.kvs[j]; - i += 1; - j += 1 - }; - - { - kvs = mergedKVs; - var count = leftData.count + 1 + rightData.count - } - }; - - func mergeChildren(leftChildren : [var ?Node], rightChildren : [var ?Node]) : [var ?Node] { - let mergedChildren = VarArray.repeat>(null, leftChildren.size()); - var i = 0; - - while (Option.isSome(leftChildren[i])) { - mergedChildren[i] := leftChildren[i]; - i += 1 - }; - - var j = 0; - while (Option.isSome(rightChildren[j])) { - mergedChildren[i] := rightChildren[j]; - i += 1; - j += 1 - }; - - mergedChildren - } - } -} diff --git a/.mops/core@2.3.1/src/Nat.mo b/.mops/core@2.3.1/src/Nat.mo deleted file mode 100644 index e93f58a..0000000 --- a/.mops/core@2.3.1/src/Nat.mo +++ /dev/null @@ -1,671 +0,0 @@ -/// Natural numbers with infinite precision. -/// -/// Most operations on natural numbers (e.g. addition) are available as built-in operators (e.g. `1 + 1`). -/// This module provides equivalent functions and `Text` conversion. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat "mo:core/Nat"; -/// ``` - -import Int "Int"; -import Prim "mo:⛔"; -import Char "Char"; -import Iter "Iter"; -import Runtime "Runtime"; -import Order "Order"; - -module { - - /// Infinite precision natural numbers. - public type Nat = Prim.Types.Nat; - - /// Converts a natural number to its textual representation. Textual - /// representation _do not_ contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toText(1234) == "1234"; - /// ``` - public let toText : (self : Nat) -> Text = Int.toText; - - /// Creates a natural number from its textual representation. Returns `null` - /// if the input is not a valid natural number. - /// - /// The textual representation _must not_ contain underscores. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromText("1234") == ?1234; - /// ``` - public func fromText(text : Text) : ?Nat { - if (text == "") { - return null - }; - var n = 0; - for (c in text.chars()) { - if (Char.isDigit(c)) { - let charAsNat = Prim.nat32ToNat(Prim.charToNat32(c) -% Prim.charToNat32('0')); - n := n * 10 + charAsNat - } else { - return null - } - }; - ?n - }; - - /// Creates a natural number from its textual representation. Returns `null` - /// if the input is not a valid natural number. - /// - /// The textual representation _must not_ contain underscores. - /// - /// This functions is meant to be used with contextual-dot notation. - /// - /// Example: - /// ```motoko include=import - /// assert "1234".toNat() == ?1234; - /// ``` - public let toNat : (self : Text) -> ?Nat = fromText; - - /// Converts an integer to a natural number. Traps if the integer is negative. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromInt(1234) == (1234 : Nat); - /// ``` - /// @deprecated M0235 - public func fromInt(int : Int) : Nat { - if (int < 0) { - Runtime.trap("Nat.fromInt(): negative input value") - } else { - Int.abs(int) - } - }; - - /// Conversion to Float. May result in `Inf`. - /// - /// Note: The floating point number may be imprecise for large Nat values. - /// Returns `inf` if the integer is greater than the maximum floating point number. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toFloat(123) == 123.0; - /// ``` - public let toFloat : (self : Nat) -> Float = Int.toFloat; - - /// Converts a natural number to an integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toInt(1234) == 1234; - /// ``` - public func toInt(self : Nat) : Int { - self : Int - }; - - /// Converts an unsigned integer with infinite precision to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toNat8(123) == (123 : Nat8); - /// ``` - public let toNat8 : (self : Nat) -> Nat8 = Prim.natToNat8; - - /// Converts an unsigned integer with infinite precision to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toNat16(123) == (123 : Nat16); - /// ``` - public let toNat16 : (self : Nat) -> Nat16 = Prim.natToNat16; - - /// Converts an unsigned integer with infinite precision to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toNat32(123) == (123 : Nat32); - /// ``` - public let toNat32 : (self : Nat) -> Nat32 = Prim.natToNat32; - - /// Converts an unsigned integer with infinite precision to a 64-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toNat64(123) == (123 : Nat64); - /// ``` - public let toNat64 : (self : Nat) -> Nat64 = Prim.natToNat64; - - /// Converts an 8-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromNat8(123) == (123 : Nat); - /// ``` - public let fromNat8 : Nat8 -> Nat = Prim.nat8ToNat; - - /// Converts a 16-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromNat16(123) == (123 : Nat); - /// ``` - public let fromNat16 : Nat16 -> Nat = Prim.nat16ToNat; - - /// Converts a 32-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromNat32(123) == (123 : Nat); - /// ``` - public let fromNat32 : Nat32 -> Nat = Prim.nat32ToNat; - - /// Converts a 64-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromNat64(123) == (123 : Nat); - /// ``` - public let fromNat64 : Nat64 -> Nat = Prim.nat64ToNat; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.min(1, 2) == 1; - /// ``` - public func min(x : Nat, y : Nat) : Nat { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.max(1, 2) == 2; - /// ``` - public func max(x : Nat, y : Nat) : Nat { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.equal(1, 1); - /// assert 1 == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a = 111; - /// let b = 222; - /// assert not Nat.equal(a, b); - /// ``` - public func equal(x : Nat, y : Nat) : Bool { x == y }; - - /// Inequality function for Nat types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.notEqual(1, 2); - /// assert 1 != 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat, y : Nat) : Bool { x != y }; - - /// "Less than" function for Nat types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.less(1, 2); - /// assert 1 < 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat, y : Nat) : Bool { x < y }; - - /// "Less than or equal" function for Nat types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.lessOrEqual(1, 2); - /// assert 1 <= 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat, y : Nat) : Bool { x <= y }; - - /// "Greater than" function for Nat types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.greater(2, 1); - /// assert 2 > 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat, y : Nat) : Bool { x > y }; - - /// "Greater than or equal" function for Nat types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.greaterOrEqual(2, 1); - /// assert 2 >= 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat, y : Nat) : Bool { x >= y }; - - /// General purpose comparison function for `Nat`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1], Nat.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat, y : Nat) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. This operator will never overflow - /// because `Nat` is infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.add(1, 2) == 3; - /// assert 1 + 2 == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat.add) == 6; - /// ``` - public func add(x : Nat, y : Nat) : Nat { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow below `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.sub(2, 1) == 1; - /// // Add a type annotation to avoid a warning about the subtraction - /// assert 2 - 1 : Nat == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 10, Nat.sub) == 4; - /// ``` - public func sub(x : Nat, y : Nat) : Nat { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. This operator will never - /// overflow because `Nat` is infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.mul(2, 3) == 6; - /// assert 2 * 3 == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat.mul) == 6; - /// ``` - public func mul(x : Nat, y : Nat) : Nat { x * y }; - - /// Returns the unsigned integer division of `x` by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// The quotient is rounded down, which is equivalent to truncating the - /// decimal places of the quotient. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.div(6, 2) == 3; - /// assert 6 / 2 == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat, y : Nat) : Nat { x / y }; - - /// Returns the remainder of unsigned integer division of `x` by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.rem(6, 4) == 2; - /// assert 6 % 4 == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat, y : Nat) : Nat { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. Traps when `y > 2^32`. This operator - /// will never overflow because `Nat` is infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.pow(2, 3) == 8; - /// assert 2 ** 3 == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat, y : Nat) : Nat { x ** y }; - - /// Returns the (conceptual) bitwise shift left of `x` by `y`, `x * (2 ** y)`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.bitshiftLeft(1, 3) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in absence - /// of the `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. While `Nat` is not defined in terms - /// of bit patterns, conceptually it can be regarded as such, and the operation - /// is provided as a high-performance version of the corresponding arithmetic - /// rule. - public let bitshiftLeft : (x : Nat, y : Nat32) -> Nat = Prim.shiftLeft; - - /// Returns the (conceptual) bitwise shift right of `x` by `y`, `x / (2 ** y)`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.bitshiftRight(8, 3) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in absence - /// of the `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. While `Nat` is not defined in terms - /// of bit patterns, conceptually it can be regarded as such, and the operation - /// is provided as a high-performance version of the corresponding arithmetic - /// rule. - public let bitshiftRight : (x : Nat, y : Nat32) -> Nat = Prim.shiftRight; - - /// Returns an iterator over `Nat` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat, toExclusive : Nat) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat { - if (n >= toExclusive) { - return null - }; - let current = n; - n += 1; - ?current - } - } - } - }; - - /// Returns an iterator over `Nat` values from the first to second argument with an exclusive upper bound, - /// incrementing by the specified step size. The step can be positive or negative. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// // Positive step - /// let iter1 = Nat.rangeBy(1, 7, 2); - /// assert iter1.next() == ?1; - /// assert iter1.next() == ?3; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// // Negative step - /// let iter2 = Nat.rangeBy(7, 1, -2); - /// assert iter2.next() == ?7; - /// assert iter2.next() == ?5; - /// assert iter2.next() == ?3; - /// assert iter2.next() == null; - /// ``` - /// - /// If `step` is 0 or if the iteration would not progress towards the bound, returns an empty iterator. - public func rangeBy(fromInclusive : Nat, toExclusive : Nat, step : Int) : Iter.Iter { - if (step == 0 or (step > 0 and fromInclusive >= toExclusive) or (step < 0 and fromInclusive <= toExclusive)) { - Iter.empty() - } else if (step > 0) { - object { - let stepMagnitude = Int.abs(step); - var n = fromInclusive; - public func next() : ?Nat { - if (n >= toExclusive) { - return null - }; - let current = n; - n += stepMagnitude; - ?current - } - } - } else { - object { - let stepMagnitude = Int.abs(step); - var n = fromInclusive; - public func next() : ?Nat { - if (n <= toExclusive) { - return null - }; - let current = n; - if (stepMagnitude > n) { - n := 0 - } else { - n -= stepMagnitude - }; - ?current - } - } - } - }; - - /// Returns an iterator over the integers from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.rangeInclusive(3, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat, to : Nat) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - public func next() : ?Nat { - if (n > to) { - return null - }; - let current = n; - n += 1; - ?current - } - } - } - }; - - /// Returns an iterator over the integers from the first to second argument, inclusive, - /// incrementing by the specified step size. The step can be positive or negative. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// // Positive step - /// let iter1 = Nat.rangeByInclusive(1, 7, 2); - /// assert iter1.next() == ?1; - /// assert iter1.next() == ?3; - /// assert iter1.next() == ?5; - /// assert iter1.next() == ?7; - /// assert iter1.next() == null; - /// - /// // Negative step - /// let iter2 = Nat.rangeByInclusive(7, 1, -2); - /// assert iter2.next() == ?7; - /// assert iter2.next() == ?5; - /// assert iter2.next() == ?3; - /// assert iter2.next() == ?1; - /// assert iter2.next() == null; - /// ``` - /// - /// If `from == to`, return an iterator which only returns that value. - /// - /// Otherwise, if `step` is 0 or if the iteration would not progress towards the bound, returns an empty iterator. - public func rangeByInclusive(from : Nat, to : Nat, step : Int) : Iter.Iter { - if (from == to) { - Iter.singleton(from) - } else if (step == 0 or (step > 0 and from > to) or (step < 0 and from < to)) { - Iter.empty() - } else if (step > 0) { - object { - let stepMagnitude = Int.abs(step); - var n = from; - public func next() : ?Nat { - if (n > to) { - return null - }; - let current = n; - n += stepMagnitude; - ?current - } - } - } else { - object { - let stepMagnitude = Int.abs(step); - var n = from; - var done = false; - public func next() : ?Nat { - if (done) { - null - } else { - let current = n; - if (n < to + stepMagnitude) { - done := true - } else { - n -= stepMagnitude - }; - ?current - } - } - } - } - }; - - /// Returns an infinite iterator over all possible `Nat` values. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter = object { - var n = 0; - public func next() : ?Nat { - let current = n; - n += 1; - ?current - } - }; - -} diff --git a/.mops/core@2.3.1/src/Nat16.mo b/.mops/core@2.3.1/src/Nat16.mo deleted file mode 100644 index 4b1195d..0000000 --- a/.mops/core@2.3.1/src/Nat16.mo +++ /dev/null @@ -1,705 +0,0 @@ -/// Utility functions on 16-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat16 "mo:core/Nat16"; -/// ``` -import Nat "Nat"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 16-bit natural numbers. - public type Nat16 = Prim.Types.Nat16; - - /// Maximum 16-bit natural number. `2 ** 16 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.maxValue == (65535 : Nat16); - /// ``` - public let maxValue : Nat16 = 65535; - - /// Converts a 16-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toNat(123) == (123 : Nat); - /// ``` - public let toNat : (self : Nat16) -> Nat = Prim.nat16ToNat; - - /// Converts an unsigned integer with infinite precision to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromNat(123) == (123 : Nat16); - /// ``` - public let fromNat : Nat -> Nat16 = Prim.natToNat16; - - /// Converts an 8-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromNat8(123) == (123 : Nat16); - /// ``` - /// @deprecated M0235 - public let fromNat8 : (x : Nat8) -> Nat16 = Prim.nat8ToNat16; - - /// Converts a 16-bit unsigned integer to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toNat8(123) == (123 : Nat8); - /// ``` - public let toNat8 : (self : Nat16) -> Nat8 = Prim.nat16ToNat8; - - /// Converts a 32-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromNat32(123) == (123 : Nat16); - /// ``` - /// @deprecated M0235 - public let fromNat32 : (x : Nat32) -> Nat16 = Prim.nat32ToNat16; - - /// Converts a 16-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toNat32(123) == (123 : Nat32); - /// ``` - public let toNat32 : (self : Nat16) -> Nat32 = Prim.nat16ToNat32; - - /// Converts a 64-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromNat64(123) == (123 : Nat16); - /// ``` - /// @deprecated M0235 - public func fromNat64(x : Nat64) : Nat16 { - Prim.nat32ToNat16(Prim.nat64ToNat32(x)) - }; - - /// Converts a 16-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toNat64(123) == (123 : Nat64); - /// ``` - public func toNat64(self : Nat16) : Nat64 { - Prim.nat32ToNat64(Prim.nat16ToNat32(self)) - }; - - /// Converts a signed integer with infinite precision to a 16-bit unsigned integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromIntWrap(123 : Int) == (123 : Nat16); - /// ``` - public let fromIntWrap : Int -> Nat16 = Prim.intToNat16Wrap; - - /// Converts `x` to its textual representation. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toText(1234) == ("1234" : Text); - /// ``` - public func toText(self : Nat16) : Text { - Nat.toText(toNat(self)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.min(123, 200) == (123 : Nat16); - /// ``` - public func min(x : Nat16, y : Nat16) : Nat16 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.max(123, 200) == (200 : Nat16); - /// ``` - public func max(x : Nat16, y : Nat16) : Nat16 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat16 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.equal(1, 1); - /// assert (1 : Nat16) == (1 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Nat16 = 111; - /// let b : Nat16 = 222; - /// assert not Nat16.equal(a, b); - /// ``` - public func equal(x : Nat16, y : Nat16) : Bool { x == y }; - - /// Inequality function for Nat16 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.notEqual(1, 2); - /// assert (1 : Nat16) != (2 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat16, y : Nat16) : Bool { x != y }; - - /// "Less than" function for Nat16 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.less(1, 2); - /// assert (1 : Nat16) < (2 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat16, y : Nat16) : Bool { x < y }; - - /// "Less than or equal" function for Nat16 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.lessOrEqual(1, 2); - /// assert (1 : Nat16) <= (2 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat16, y : Nat16) : Bool { x <= y }; - - /// "Greater than" function for Nat16 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.greater(2, 1); - /// assert (2 : Nat16) > (1 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat16, y : Nat16) : Bool { x > y }; - - /// "Greater than or equal" function for Nat16 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.greaterOrEqual(2, 1); - /// assert (2 : Nat16) >= (1 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat16, y : Nat16) : Bool { - x >= y - }; - - /// General purpose comparison function for `Nat16`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1] : [Nat16], Nat16.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat16, y : Nat16) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.add(1, 2) == 3; - /// assert (1 : Nat16) + (2 : Nat16) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat16.add) == 6; - /// ``` - public func add(x : Nat16, y : Nat16) : Nat16 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.sub(2, 1) == 1; - /// assert (2 : Nat16) - (1 : Nat16) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 20, Nat16.sub) == 14; - /// ``` - public func sub(x : Nat16, y : Nat16) : Nat16 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.mul(2, 3) == 6; - /// assert (2 : Nat16) * (3 : Nat16) == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat16.mul) == 6; - /// ``` - public func mul(x : Nat16, y : Nat16) : Nat16 { x * y }; - - /// Returns the quotient of `x` divided by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.div(6, 2) == 3; - /// assert (6 : Nat16) / (2 : Nat16) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat16, y : Nat16) : Nat16 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.rem(6, 4) == 2; - /// assert (6 : Nat16) % (4 : Nat16) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat16, y : Nat16) : Nat16 { x % y }; - - /// Returns the power of `x` to `y`, `x ** y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.pow(2, 3) == 8; - /// assert (2 : Nat16) ** (3 : Nat16) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat16, y : Nat16) : Nat16 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitnot(0) == 65535; - /// assert ^(0 : Nat16) == 65535; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat16) : Nat16 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitand(0, 1) == 0; - /// assert (0 : Nat16) & (1 : Nat16) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat16, y : Nat16) : Nat16 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitor(0, 1) == 1; - /// assert (0 : Nat16) | (1 : Nat16) == 1; - /// ``` - public func bitor(x : Nat16, y : Nat16) : Nat16 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitxor(0, 1) == 1; - /// assert (0 : Nat16) ^ (1 : Nat16) == 1; - /// ``` - public func bitxor(x : Nat16, y : Nat16) : Nat16 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitshiftLeft(1, 3) == 8; - /// assert (1 : Nat16) << (3 : Nat16) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat16, y : Nat16) : Nat16 { - x << y - }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitshiftRight(8, 3) == 1; - /// assert (8 : Nat16) >> (3 : Nat16) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat16, y : Nat16) : Nat16 { - x >> y - }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitrotLeft(2, 1) == 4; - /// assert (2 : Nat16) <<> (1 : Nat16) == 4; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat16, y : Nat16) : Nat16 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitrotRight(1, 1) == 32768; - /// assert (1 : Nat16) <>> (1 : Nat16) == 32768; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat16, y : Nat16) : Nat16 { - x <>> y - }; - - /// Returns the value of bit `p mod 16` in `x`, `(x & 2^(p mod 16)) == 2^(p mod 16)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bittest(5, 2); - /// ``` - public func bittest(x : Nat16, p : Nat) : Bool { - Prim.btstNat16(x, Prim.natToNat16(p)) - }; - - /// Returns the value of setting bit `p mod 16` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitset(0, 2) == 4; - /// ``` - public func bitset(x : Nat16, p : Nat) : Nat16 { - x | (1 << Prim.natToNat16(p)) - }; - - /// Returns the value of clearing bit `p mod 16` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitclear(5, 2) == 1; - /// ``` - public func bitclear(x : Nat16, p : Nat) : Nat16 { - x & ^(1 << Prim.natToNat16(p)) - }; - - /// Returns the value of flipping bit `p mod 16` in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitflip(5, 2) == 1; - /// ``` - public func bitflip(x : Nat16, p : Nat) : Nat16 { - x ^ (1 << Prim.natToNat16(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitcountNonZero(5) == 2; - /// ``` - public let bitcountNonZero : (x : Nat16) -> Nat16 = Prim.popcntNat16; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitcountLeadingZero(5) == 13; - /// ``` - public let bitcountLeadingZero : (x : Nat16) -> Nat16 = Prim.clzNat16; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitcountTrailingZero(5) == 0; - /// ``` - public let bitcountTrailingZero : (x : Nat16) -> Nat16 = Prim.ctzNat16; - - /// Returns the upper (i.e. most significant) and lower (least significant) byte of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.explode 0xaa88 == (170, 136); - /// ``` - public let explode : (x : Nat16) -> (msb : Nat8, lsb : Nat8) = Prim.explodeNat16; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.addWrap(65532, 5) == 1; - /// assert (65532 : Nat16) +% (5 : Nat16) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat16, y : Nat16) : Nat16 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.subWrap(1, 2) == 65535; - /// assert (1 : Nat16) -% (2 : Nat16) == 65535; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat16, y : Nat16) : Nat16 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.mulWrap(655, 101) == 619; - /// assert (655 : Nat16) *% (101 : Nat16) == 619; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat16, y : Nat16) : Nat16 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.powWrap(2, 16) == 0; - /// assert (2 : Nat16) **% (16 : Nat16) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat16, y : Nat16) : Nat16 { x **% y }; - - /// Returns an iterator over `Nat16` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat16, toExclusive : Nat16) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat16 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Nat16` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat16, to : Nat16) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Nat16 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Nat16 values, from 0 to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(0, maxValue) - }; - -} diff --git a/.mops/core@2.3.1/src/Nat32.mo b/.mops/core@2.3.1/src/Nat32.mo deleted file mode 100644 index f4759f1..0000000 --- a/.mops/core@2.3.1/src/Nat32.mo +++ /dev/null @@ -1,724 +0,0 @@ -/// Utility functions on 32-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat32 "mo:core/Nat32"; -/// ``` -import Nat "Nat"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 32-bit natural numbers. - public type Nat32 = Prim.Types.Nat32; - - /// Maximum 32-bit natural number. `2 ** 32 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.maxValue == (4294967295 : Nat32); - /// ``` - public let maxValue : Nat32 = 4294967295; - - /// Converts a 32-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toNat(123) == (123 : Nat); - /// ``` - public let toNat : (self : Nat32) -> Nat = Prim.nat32ToNat; - - /// Converts an unsigned integer with infinite precision to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromNat(123) == (123 : Nat32); - /// ``` - public let fromNat : Nat -> Nat32 = Prim.natToNat32; - - /// Converts a 32-bit unsigned integer to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toNat8(123) == (123 : Nat8); - /// ``` - public func toNat8(self : Nat32) : Nat8 { - Prim.nat16ToNat8(Prim.nat32ToNat16(self)) - }; - - /// Converts an 8-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromNat8(123) == (123 : Nat32); - /// ``` - /// @deprecated M0235 - public func fromNat8(x : Nat8) : Nat32 { - Prim.nat16ToNat32(Prim.nat8ToNat16(x)) - }; - - /// Converts a 16-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromNat16(123) == (123 : Nat32); - /// ``` - /// @deprecated M0235 - public let fromNat16 : (x : Nat16) -> Nat32 = Prim.nat16ToNat32; - - /// Converts a 32-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toNat16(123) == (123 : Nat16); - /// ``` - public let toNat16 : (self : Nat32) -> Nat16 = Prim.nat32ToNat16; - - /// Converts a 64-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromNat64(123) == (123 : Nat32); - /// ``` - /// @deprecated M0235 - public let fromNat64 : (x : Nat64) -> Nat32 = Prim.nat64ToNat32; - - /// Converts a 32-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toNat64(123) == (123 : Nat64); - /// ``` - public let toNat64 : (self : Nat32) -> Nat64 = Prim.nat32ToNat64; - - /// Converts a signed integer with infinite precision to a 32-bit unsigned integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromIntWrap(123) == (123 : Nat32); - /// ``` - public let fromIntWrap : Int -> Nat32 = Prim.intToNat32Wrap; - - /// Convert a Nat32 `char` to a Char in its Unicode representation. - /// - /// Example: - /// ```motoko include=import - /// let unicode = Nat32.toChar(65); - /// assert unicode == 'A'; - /// ``` - public let toChar : (self : Nat32) -> Char = Prim.nat32ToChar; - - /// Converts `x` to its textual representation. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toText(1234) == ("1234" : Text); - /// ``` - public func toText(self : Nat32) : Text { - Nat.toText(toNat(self)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.min(123, 456) == (123 : Nat32); - /// ``` - public func min(x : Nat32, y : Nat32) : Nat32 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.max(123, 456) == (456 : Nat32); - /// ``` - public func max(x : Nat32, y : Nat32) : Nat32 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat32 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.equal(1, 1); - /// assert (1 : Nat32) == (1 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Nat32 = 111; - /// let b : Nat32 = 222; - /// assert not Nat32.equal(a, b); - /// ``` - public func equal(x : Nat32, y : Nat32) : Bool { x == y }; - - /// Inequality function for Nat32 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.notEqual(1, 2); - /// assert (1 : Nat32) != (2 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat32, y : Nat32) : Bool { x != y }; - - /// "Less than" function for Nat32 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.less(1, 2); - /// assert (1 : Nat32) < (2 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat32, y : Nat32) : Bool { x < y }; - - /// "Less than or equal" function for Nat32 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.lessOrEqual(1, 2); - /// assert (1 : Nat32) <= (2 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat32, y : Nat32) : Bool { x <= y }; - - /// "Greater than" function for Nat32 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.greater(2, 1); - /// assert (2 : Nat32) > (1 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat32, y : Nat32) : Bool { x > y }; - - /// "Greater than or equal" function for Nat32 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.greaterOrEqual(2, 1); - /// assert (2 : Nat32) >= (1 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat32, y : Nat32) : Bool { - x >= y - }; - - /// General purpose comparison function for `Nat32`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1] : [Nat32], Nat32.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat32, y : Nat32) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.add(1, 2) == 3; - /// assert (1 : Nat32) + (2 : Nat32) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat32.add) == 6; - /// ``` - public func add(x : Nat32, y : Nat32) : Nat32 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.sub(2, 1) == 1; - /// assert (2 : Nat32) - (1 : Nat32) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 20, Nat32.sub) == 14; - /// ``` - public func sub(x : Nat32, y : Nat32) : Nat32 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.mul(2, 3) == 6; - /// assert (2 : Nat32) * (3 : Nat32) == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat32.mul) == 6; - /// ``` - public func mul(x : Nat32, y : Nat32) : Nat32 { x * y }; - - /// Returns the division of `x by y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.div(6, 2) == 3; - /// assert (6 : Nat32) / (2 : Nat32) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat32, y : Nat32) : Nat32 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.rem(6, 4) == 2; - /// assert (6 : Nat32) % (4 : Nat32) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat32, y : Nat32) : Nat32 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.pow(2, 3) == 8; - /// assert (2 : Nat32) ** (3 : Nat32) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat32, y : Nat32) : Nat32 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitnot(0) == 4294967295; - /// assert ^(0 : Nat32) == 4294967295; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat32) : Nat32 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitand(1, 3) == 1; - /// assert (1 : Nat32) & (3 : Nat32) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat32, y : Nat32) : Nat32 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitor(1, 3) == 3; - /// assert (1 : Nat32) | (3 : Nat32) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Nat32, y : Nat32) : Nat32 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitxor(1, 3) == 2; - /// assert (1 : Nat32) ^ (3 : Nat32) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Nat32, y : Nat32) : Nat32 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitshiftLeft(1, 3) == 8; - /// assert (1 : Nat32) << (3 : Nat32) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat32, y : Nat32) : Nat32 { - x << y - }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitshiftRight(8, 3) == 1; - /// assert (8 : Nat32) >> (3 : Nat32) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat32, y : Nat32) : Nat32 { - x >> y - }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitrotLeft(1, 3) == 8; - /// assert (1 : Nat32) <<> (3 : Nat32) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat32, y : Nat32) : Nat32 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitrotRight(1, 1) == 2147483648; - /// assert (1 : Nat32) <>> (1 : Nat32) == 2147483648; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat32, y : Nat32) : Nat32 { - x <>> y - }; - - /// Returns the value of bit `p mod 32` in `x`, `(x & 2^(p mod 32)) == 2^(p mod 32)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bittest(5, 2); - /// ``` - public func bittest(x : Nat32, p : Nat) : Bool { - Prim.btstNat32(x, Prim.natToNat32(p)) - }; - - /// Returns the value of setting bit `p mod 32` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitset(5, 1) == 7; - /// ``` - public func bitset(x : Nat32, p : Nat) : Nat32 { - x | (1 << Prim.natToNat32(p)) - }; - - /// Returns the value of clearing bit `p mod 32` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitclear(5, 2) == 1; - /// ``` - public func bitclear(x : Nat32, p : Nat) : Nat32 { - x & ^(1 << Prim.natToNat32(p)) - }; - - /// Returns the value of flipping bit `p mod 32` in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitflip(5, 2) == 1; - /// ``` - public func bitflip(x : Nat32, p : Nat) : Nat32 { - x ^ (1 << Prim.natToNat32(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitcountNonZero(5) == 2; - /// ``` - public let bitcountNonZero : (x : Nat32) -> Nat32 = Prim.popcntNat32; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitcountLeadingZero(5) == 29; - /// ``` - public let bitcountLeadingZero : (x : Nat32) -> Nat32 = Prim.clzNat32; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitcountTrailingZero(16) == 4; - /// ``` - public let bitcountTrailingZero : (x : Nat32) -> Nat32 = Prim.ctzNat32; - - /// Returns the upper (i.e. most significant), lower (least significant) - /// and in-between bytes of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.explode 0xaa885511 == (170, 136, 85, 17); - /// ``` - public let explode : (x : Nat32) -> (msb : Nat8, Nat8, Nat8, lsb : Nat8) = Prim.explodeNat32; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.addWrap(4294967295, 1) == 0; - /// assert (4294967295 : Nat32) +% (1 : Nat32) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat32, y : Nat32) : Nat32 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.subWrap(0, 1) == 4294967295; - /// assert (0 : Nat32) -% (1 : Nat32) == 4294967295; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat32, y : Nat32) : Nat32 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.mulWrap(2147483648, 2) == 0; - /// assert (2147483648 : Nat32) *% (2 : Nat32) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat32, y : Nat32) : Nat32 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.powWrap(2, 32) == 0; - /// assert (2 : Nat32) **% (32 : Nat32) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat32, y : Nat32) : Nat32 { x **% y }; - - /// Returns an iterator over `Nat32` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat32, toExclusive : Nat32) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat32 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Nat32` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat32, to : Nat32) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Nat32 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Nat32 values, from 0 to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(0, maxValue) - }; - -} diff --git a/.mops/core@2.3.1/src/Nat64.mo b/.mops/core@2.3.1/src/Nat64.mo deleted file mode 100644 index e16a9f1..0000000 --- a/.mops/core@2.3.1/src/Nat64.mo +++ /dev/null @@ -1,719 +0,0 @@ -/// Utility functions on 64-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat64 "mo:core/Nat64"; -/// ``` -import Nat "Nat"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 64-bit natural numbers. - public type Nat64 = Prim.Types.Nat64; - - /// Maximum 64-bit natural number. `2 ** 64 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.maxValue == (18446744073709551615 : Nat64); - /// ``` - public let maxValue : Nat64 = 18446744073709551615; - - /// Converts a 64-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toNat(123) == (123 : Nat); - /// ``` - public let toNat : (self : Nat64) -> Nat = Prim.nat64ToNat; - - /// Converts an unsigned integer with infinite precision to a 64-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromNat(123) == (123 : Nat64); - /// ``` - public let fromNat : Nat -> Nat64 = Prim.natToNat64; - - /// Converts a 64-bit unsigned integer to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toNat8(123) == (123 : Nat8); - /// ``` - public func toNat8(self : Nat64) : Nat8 { - Prim.nat16ToNat8(Prim.nat32ToNat16(Prim.nat64ToNat32(self))) - }; - - /// Converts a 16-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromNat16(123) == (123 : Nat64); - /// ``` - /// @deprecated M0235 - public func fromNat16(x : Nat16) : Nat64 { - Prim.nat32ToNat64(Prim.nat16ToNat32(x)) - }; - - /// Converts a 64-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toNat16(123) == (123 : Nat16); - /// ``` - public func toNat16(self : Nat64) : Nat16 { - Prim.nat32ToNat16(Prim.nat64ToNat32(self)) - }; - - /// Converts an 8-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromNat8(123) == (123 : Nat64); - /// ``` - /// @deprecated M0235 - public func fromNat8(x : Nat8) : Nat64 { - Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(x))) - }; - - /// Converts a 32-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromNat32(123) == (123 : Nat64); - /// ``` - /// @deprecated M0235 - public let fromNat32 : (x : Nat32) -> Nat64 = Prim.nat32ToNat64; - - /// Converts a 64-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toNat32(123) == (123 : Nat32); - /// ``` - public let toNat32 : (self : Nat64) -> Nat32 = Prim.nat64ToNat32; - - /// Converts a signed integer with infinite precision to a 64-bit unsigned integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromIntWrap(123) == (123 : Nat64); - /// ``` - public let fromIntWrap : Int -> Nat64 = Prim.intToNat64Wrap; - - /// Converts `x` to its textual representation. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toText(1234) == ("1234" : Text); - /// ``` - public func toText(self : Nat64) : Text { - Nat.toText(toNat(self)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.min(123, 456) == (123 : Nat64); - /// ``` - public func min(x : Nat64, y : Nat64) : Nat64 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.max(123, 456) == (456 : Nat64); - /// ``` - public func max(x : Nat64, y : Nat64) : Nat64 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat64 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.equal(1, 1); - /// assert (1 : Nat64) == (1 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Nat64 = 111; - /// let b : Nat64 = 222; - /// assert not Nat64.equal(a, b); - /// ``` - public func equal(x : Nat64, y : Nat64) : Bool { x == y }; - - /// Inequality function for Nat64 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.notEqual(1, 2); - /// assert (1 : Nat64) != (2 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat64, y : Nat64) : Bool { x != y }; - - /// "Less than" function for Nat64 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.less(1, 2); - /// assert (1 : Nat64) < (2 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat64, y : Nat64) : Bool { x < y }; - - /// "Less than or equal" function for Nat64 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.lessOrEqual(1, 2); - /// assert (1 : Nat64) <= (2 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat64, y : Nat64) : Bool { x <= y }; - - /// "Greater than" function for Nat64 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.greater(2, 1); - /// assert (2 : Nat64) > (1 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat64, y : Nat64) : Bool { x > y }; - - /// "Greater than or equal" function for Nat64 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.greaterOrEqual(2, 1); - /// assert (2 : Nat64) >= (1 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat64, y : Nat64) : Bool { - x >= y - }; - - /// General purpose comparison function for `Nat64`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1] : [Nat64], Nat64.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat64, y : Nat64) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.add(1, 2) == 3; - /// assert (1 : Nat64) + (2 : Nat64) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat64.add) == 6; - /// ``` - public func add(x : Nat64, y : Nat64) : Nat64 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.sub(3, 1) == 2; - /// assert (3 : Nat64) - (1 : Nat64) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 10, Nat64.sub) == 4; - /// ``` - public func sub(x : Nat64, y : Nat64) : Nat64 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.mul(2, 3) == 6; - /// assert (2 : Nat64) * (3 : Nat64) == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat64.mul) == 6; - /// ``` - public func mul(x : Nat64, y : Nat64) : Nat64 { x * y }; - - /// Returns the quotient of `x` divided by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.div(6, 2) == 3; - /// assert (6 : Nat64) / (2 : Nat64) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat64, y : Nat64) : Nat64 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.rem(6, 4) == 2; - /// assert (6 : Nat64) % (4 : Nat64) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat64, y : Nat64) : Nat64 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.pow(2, 3) == 8; - /// assert (2 : Nat64) ** (3 : Nat64) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat64, y : Nat64) : Nat64 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitnot(0) == 18446744073709551615; - /// assert ^(0 : Nat64) == 18446744073709551615; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat64) : Nat64 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitand(1, 3) == 1; - /// assert (1 : Nat64) & (3 : Nat64) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat64, y : Nat64) : Nat64 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitor(1, 3) == 3; - /// assert (1 : Nat64) | (3 : Nat64) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Nat64, y : Nat64) : Nat64 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitxor(1, 3) == 2; - /// assert (1 : Nat64) ^ (3 : Nat64) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Nat64, y : Nat64) : Nat64 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitshiftLeft(1, 3) == 8; - /// assert (1 : Nat64) << (3 : Nat64) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat64, y : Nat64) : Nat64 { - x << y - }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitshiftRight(8, 3) == 1; - /// assert (8 : Nat64) >> (3 : Nat64) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat64, y : Nat64) : Nat64 { - x >> y - }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitrotLeft(1, 3) == 8; - /// assert (1 : Nat64) <<> (3 : Nat64) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat64, y : Nat64) : Nat64 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitrotRight(8, 3) == 1; - /// assert (8 : Nat64) <>> (3 : Nat64) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat64, y : Nat64) : Nat64 { - x <>> y - }; - - /// Returns the value of bit `p mod 64` in `x`, `(x & 2^(p mod 64)) == 2^(p mod 64)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bittest(5, 2); - /// ``` - public func bittest(x : Nat64, p : Nat) : Bool { - Prim.btstNat64(x, Prim.natToNat64(p)) - }; - - /// Returns the value of setting bit `p mod 64` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitset(5, 1) == 7; - /// ``` - public func bitset(x : Nat64, p : Nat) : Nat64 { - x | (1 << Prim.natToNat64(p)) - }; - - /// Returns the value of clearing bit `p mod 64` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitclear(5, 2) == 1; - /// ``` - public func bitclear(x : Nat64, p : Nat) : Nat64 { - x & ^(1 << Prim.natToNat64(p)) - }; - - /// Returns the value of flipping bit `p mod 64` in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitflip(5, 2) == 1; - /// ``` - public func bitflip(x : Nat64, p : Nat) : Nat64 { - x ^ (1 << Prim.natToNat64(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitcountNonZero(5) == 2; - /// ``` - public let bitcountNonZero : (x : Nat64) -> Nat64 = Prim.popcntNat64; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitcountLeadingZero(5) == 61; - /// ``` - public let bitcountLeadingZero : (x : Nat64) -> Nat64 = Prim.clzNat64; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitcountTrailingZero(16) == 4; - /// ``` - public let bitcountTrailingZero : (x : Nat64) -> Nat64 = Prim.ctzNat64; - - /// Returns the upper (i.e. most significant), lower (least significant) - /// and in-between bytes of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.explode 0xbb772266aa885511 == (187, 119, 34, 102, 170, 136, 85, 17); - /// ``` - public let explode : (x : Nat64) -> (msb : Nat8, Nat8, Nat8, Nat8, Nat8, Nat8, Nat8, lsb : Nat8) = Prim.explodeNat64; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.addWrap(Nat64.maxValue, 1) == 0; - /// assert Nat64.maxValue +% (1 : Nat64) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat64, y : Nat64) : Nat64 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.subWrap(0, 1) == 18446744073709551615; - /// assert (0 : Nat64) -% (1 : Nat64) == 18446744073709551615; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat64, y : Nat64) : Nat64 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.mulWrap(4294967296, 4294967296) == 0; - /// assert (4294967296 : Nat64) *% (4294967296 : Nat64) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat64, y : Nat64) : Nat64 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.powWrap(2, 64) == 0; - /// assert (2 : Nat64) **% (64 : Nat64) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat64, y : Nat64) : Nat64 { x **% y }; - - /// Returns an iterator over `Nat64` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat64, toExclusive : Nat64) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat64 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Nat64` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat64, to : Nat64) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Nat64 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Nat64 values, from 0 to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(0, maxValue) - }; - -} diff --git a/.mops/core@2.3.1/src/Nat8.mo b/.mops/core@2.3.1/src/Nat8.mo deleted file mode 100644 index 429aa7e..0000000 --- a/.mops/core@2.3.1/src/Nat8.mo +++ /dev/null @@ -1,698 +0,0 @@ -/// Utility functions on 8-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat8 "mo:core/Nat8"; -/// ``` -import Nat "Nat"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 8-bit natural numbers. - public type Nat8 = Prim.Types.Nat8; - - /// Maximum 8-bit natural number. `2 ** 8 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.maxValue == (255 : Nat8); - /// ``` - public let maxValue : Nat8 = 255; - - /// Converts an 8-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toNat(123) == (123 : Nat); - /// ``` - public let toNat : (self : Nat8) -> Nat = Prim.nat8ToNat; - - /// Converts an unsigned integer with infinite precision to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromNat(123) == (123 : Nat8); - /// ``` - public let fromNat : Nat -> Nat8 = Prim.natToNat8; - - /// Converts a 16-bit unsigned integer to a 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromNat16(123) == (123 : Nat8); - /// ``` - public let fromNat16 : Nat16 -> Nat8 = Prim.nat16ToNat8; - - /// Converts an 8-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toNat16(123) == (123 : Nat16); - /// ``` - public let toNat16 : (self : Nat8) -> Nat16 = Prim.nat8ToNat16; - - /// Converts a 32-bit unsigned integer to a 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromNat32(123) == (123 : Nat8); - /// ``` - public func fromNat32(x : Nat32) : Nat8 { - Prim.nat16ToNat8(Prim.nat32ToNat16(x)) - }; - - /// Converts an 8-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toNat32(123) == (123 : Nat32); - /// ``` - public func toNat32(self : Nat8) : Nat32 { - Prim.nat16ToNat32(Prim.nat8ToNat16(self)) - }; - - /// Converts a 64-bit unsigned integer to a 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromNat64(123) == (123 : Nat8); - /// ``` - public func fromNat64(x : Nat64) : Nat8 { - Prim.nat16ToNat8(Prim.nat32ToNat16(Prim.nat64ToNat32(x))) - }; - - /// Converts an 8-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toNat64(123) == (123 : Nat64); - /// ``` - public func toNat64(self : Nat8) : Nat64 { - Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(self))) - }; - - /// Converts a signed integer with infinite precision to an 8-bit unsigned integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromIntWrap(123) == (123 : Nat8); - /// ``` - public let fromIntWrap : Int -> Nat8 = Prim.intToNat8Wrap; - - /// Converts `x` to its textual representation. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toText(123) == ("123" : Text); - /// ``` - public func toText(self : Nat8) : Text { - Nat.toText(toNat(self)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.min(123, 200) == (123 : Nat8); - /// ``` - public func min(x : Nat8, y : Nat8) : Nat8 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.max(123, 200) == (200 : Nat8); - /// ``` - public func max(x : Nat8, y : Nat8) : Nat8 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat8 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.equal(1, 1); - /// assert (1 : Nat8) == (1 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Nat8 = 111; - /// let b : Nat8 = 222; - /// assert not Nat8.equal(a, b); - /// ``` - public func equal(x : Nat8, y : Nat8) : Bool { x == y }; - - /// Inequality function for Nat8 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.notEqual(1, 2); - /// assert (1 : Nat8) != (2 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat8, y : Nat8) : Bool { x != y }; - - /// "Less than" function for Nat8 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.less(1, 2); - /// assert (1 : Nat8) < (2 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat8, y : Nat8) : Bool { x < y }; - - /// "Less than or equal" function for Nat8 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.lessOrEqual(1, 2); - /// assert 1 <= 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat8, y : Nat8) : Bool { x <= y }; - - /// "Greater than" function for Nat8 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.greater(2, 1); - /// assert (2 : Nat8) > (1 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat8, y : Nat8) : Bool { x > y }; - - /// "Greater than or equal" function for Nat8 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.greaterOrEqual(2, 1); - /// assert (2 : Nat8) >= (1 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat8, y : Nat8) : Bool { x >= y }; - - /// General purpose comparison function for `Nat8`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1] : [Nat8], Nat8.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat8, y : Nat8) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.add(1, 2) == 3; - /// assert (1 : Nat8) + (2 : Nat8) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat8.add) == 6; - /// ``` - public func add(x : Nat8, y : Nat8) : Nat8 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.sub(2, 1) == 1; - /// assert (2 : Nat8) - (1 : Nat8) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 20, Nat8.sub) == 14; - /// ``` - public func sub(x : Nat8, y : Nat8) : Nat8 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.mul(2, 3) == 6; - /// assert (2 : Nat8) * (3 : Nat8) == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat8.mul) == 6; - /// ``` - public func mul(x : Nat8, y : Nat8) : Nat8 { x * y }; - - /// Returns the quotient of `x` divided by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.div(6, 2) == 3; - /// assert (6 : Nat8) / (2 : Nat8) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat8, y : Nat8) : Nat8 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.rem(6, 4) == 2; - /// assert (6 : Nat8) % (4 : Nat8) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat8, y : Nat8) : Nat8 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.pow(2, 3) == 8; - /// assert (2 : Nat8) ** (3 : Nat8) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat8, y : Nat8) : Nat8 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitnot(0) == 255; - /// assert ^(0 : Nat8) == 255; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat8) : Nat8 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitand(3, 2) == 2; - /// assert (3 : Nat8) & (2 : Nat8) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat8, y : Nat8) : Nat8 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitor(3, 2) == 3; - /// assert (3 : Nat8) | (2 : Nat8) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Nat8, y : Nat8) : Nat8 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitxor(3, 2) == 1; - /// assert (3 : Nat8) ^ (2 : Nat8) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Nat8, y : Nat8) : Nat8 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitshiftLeft(1, 2) == 4; - /// assert (1 : Nat8) << (2 : Nat8) == 4; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat8, y : Nat8) : Nat8 { x << y }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitshiftRight(4, 2) == 1; - /// assert (4 : Nat8) >> (2 : Nat8) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat8, y : Nat8) : Nat8 { x >> y }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitrotLeft(128, 1) == 1; - /// assert (128 : Nat8) <<> (1 : Nat8) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat8, y : Nat8) : Nat8 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitrotRight(1, 1) == 128; - /// assert (1 : Nat8) <>> (1 : Nat8) == 128; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat8, y : Nat8) : Nat8 { x <>> y }; - - /// Returns the value of bit `p mod 8` in `x`, `(x & 2^(p mod 8)) == 2^(p mod 8)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bittest(5, 2); - /// ``` - public func bittest(x : Nat8, p : Nat) : Bool { - Prim.btstNat8(x, Prim.natToNat8(p)) - }; - - /// Returns the value of setting bit `p mod 8` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitset(5, 1) == 7; - /// ``` - public func bitset(x : Nat8, p : Nat) : Nat8 { - x | (1 << Prim.natToNat8(p)) - }; - - /// Returns the value of clearing bit `p mod 8` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitclear(5, 2) == 1; - /// ``` - public func bitclear(x : Nat8, p : Nat) : Nat8 { - x & ^(1 << Prim.natToNat8(p)) - }; - - /// Returns the value of flipping bit `p mod 8` in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitflip(5, 2) == 1; - /// ``` - public func bitflip(x : Nat8, p : Nat) : Nat8 { - x ^ (1 << Prim.natToNat8(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitcountNonZero(5) == 2; - /// ``` - public let bitcountNonZero : (x : Nat8) -> Nat8 = Prim.popcntNat8; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitcountLeadingZero(5) == 5; - /// ``` - public let bitcountLeadingZero : (x : Nat8) -> Nat8 = Prim.clzNat8; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitcountTrailingZero(6) == 1; - /// ``` - public let bitcountTrailingZero : (x : Nat8) -> Nat8 = Prim.ctzNat8; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.addWrap(230, 26) == 0; - /// assert (230 : Nat8) +% (26 : Nat8) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat8, y : Nat8) : Nat8 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.subWrap(0, 1) == 255; - /// assert (0 : Nat8) -% (1 : Nat8) == 255; - /// ``` - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat8, y : Nat8) : Nat8 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.mulWrap(230, 26) == 92; - /// assert (230 : Nat8) *% (26 : Nat8) == 92; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat8, y : Nat8) : Nat8 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.powWrap(2, 8) == 0; - /// assert (2 : Nat8) **% (8 : Nat8) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat8, y : Nat8) : Nat8 { x **% y }; - - /// Returns an iterator over `Nat8` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat8, toExclusive : Nat8) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat8 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Nat8` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat8, to : Nat8) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Nat8 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Nat8 values, from 0 to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(0, maxValue) - }; - -} diff --git a/.mops/core@2.3.1/src/Option.mo b/.mops/core@2.3.1/src/Option.mo deleted file mode 100644 index 27bfce6..0000000 --- a/.mops/core@2.3.1/src/Option.mo +++ /dev/null @@ -1,154 +0,0 @@ -/// Typesafe nullable values. -/// -/// Optional values can be seen as a typesafe `null`. A value of type `?Int` can -/// be constructed with either `null` or `?42`. The simplest way to get at the -/// contents of an optional is to use pattern matching: -/// -/// ```motoko -/// let optionalInt1 : ?Int = ?42; -/// let optionalInt2 : ?Int = null; -/// -/// let int1orZero : Int = switch optionalInt1 { -/// case null 0; -/// case (?int) int; -/// }; -/// assert int1orZero == 42; -/// -/// let int2orZero : Int = switch optionalInt2 { -/// case null 0; -/// case (?int) int; -/// }; -/// assert int2orZero == 0; -/// ``` -/// -/// The functions in this module capture some common operations when working -/// with optionals that can be more succinct than using pattern matching. - -import Runtime "Runtime"; -import Types "Types"; - -module { - - /// Unwraps an optional value, with a default value, i.e. `get(?x, d) = x` and - /// `get(null, d) = d`. - public func get(self : ?T, default : T) : T = switch self { - case null { default }; - case (?x_) { x_ } - }; - - /// Unwraps an optional value using a function, or returns the default, i.e. - /// `option(?x, f, d) = f x` and `option(null, f, d) = d`. - public func getMapped(self : ?T, f : T -> R, default : R) : R = switch self { - case null { default }; - case (?x_) { f(x_) } - }; - - /// Applies a function to the wrapped value. `null`'s are left untouched. - /// ```motoko - /// import Option "mo:core/Option"; - /// assert Option.map(?42, func x = x + 1) == ?43; - /// assert Option.map(null, func x = x + 1) == null; - /// ``` - public func map(self : ?T, f : T -> R) : ?R = switch self { - case null { null }; - case (?x_) { ?f(x_) } - }; - - /// Applies a function to the wrapped value, but discards the result. Use - /// `forEach` if you're only interested in the side effect `f` produces. - /// - /// ```motoko - /// import Option "mo:core/Option"; - /// var counter : Nat = 0; - /// Option.forEach(?5, func (x : Nat) { counter += x }); - /// assert counter == 5; - /// Option.forEach(null, func (x : Nat) { counter += x }); - /// assert counter == 5; - /// ``` - public func forEach(self : ?T, f : T -> ()) = switch self { - case null {}; - case (?x_) { f(x_) } - }; - - /// Applies an optional function to an optional value. Returns `null` if at - /// least one of the arguments is `null`. - public func apply(self : ?T, f : ?(T -> R)) : ?R { - switch (f, self) { - case (?f_, ?x_) { ?f_(x_) }; - case (_, _) { null } - } - }; - - /// Applies a function to an optional value. Returns `null` if the argument is - /// `null`, or the function returns `null`. - public func chain(self : ?T, f : T -> ?R) : ?R { - switch (self) { - case (?x_) { f(x_) }; - case (null) { null } - } - }; - - /// Given an optional optional value, removes one layer of optionality. - /// ```motoko - /// import Option "mo:core/Option"; - /// assert Option.flatten(?(?(42))) == ?42; - /// assert Option.flatten(?(null)) == null; - /// assert Option.flatten(null) == null; - /// ``` - public func flatten(self : ??T) : ?T { - chain(self, func(x_ : ?T) : ?T = x_) - }; - - /// Creates an optional value from a definite value. - /// ```motoko - /// import Option "mo:core/Option"; - /// assert Option.some(42) == ?42; - /// ``` - public func some(self : T) : ?T = ?self; - - /// Returns true if the argument is not `null`, otherwise returns false. - public func isSome(self : ?Any) : Bool { - self != null - }; - - /// Returns true if the argument is `null`, otherwise returns false. - public func isNull(self : ?Any) : Bool { - self == null - }; - - /// Returns true if the optional arguments are equal according to the equality function provided, otherwise returns false. - public func equal(self : ?T, other : ?T, eq : (implicit : (equal : (T, T) -> Bool))) : Bool = switch (self, other) { - case (null, null) { true }; - case (?x_, ?y_) { eq(x_, y_) }; - case (_, _) { false } - }; - - /// Compares two optional values using the provided comparison function. - /// - /// Returns: - /// - `#equal` if both values are `null`, - /// - `#less` if the first value is `null` and the second is not, - /// - `#greater` if the first value is not `null` and the second is, - /// - the result of the comparison function when both values are not `null`. - public func compare(self : ?T, other : ?T, compare : (implicit : (T, T) -> Types.Order)) : Types.Order = switch (self, other) { - case (null, null) #equal; - case (null, _) #less; - case (_, null) #greater; - case (?x_, ?y_) { compare(x_, y_) } - }; - - /// Unwraps an optional value, i.e. `unwrap(?x) = x`. - /// - /// `Option.unwrap()` fails if the argument is null. Consider using a `switch` or `do?` expression instead. - public func unwrap(self : ?T) : T = switch self { - case null { Runtime.trap("Option.unwrap()") }; - case (?x_) { x_ } - }; - - /// Returns the textural representation of an optional value for debugging purposes. - public func toText(self : ?T, toText : (implicit : T -> Text)) : Text = switch self { - case null { "null" }; - case (?x_) { "?" # toText(x_) } - }; - -} diff --git a/.mops/core@2.3.1/src/Order.mo b/.mops/core@2.3.1/src/Order.mo deleted file mode 100644 index d708a11..0000000 --- a/.mops/core@2.3.1/src/Order.mo +++ /dev/null @@ -1,62 +0,0 @@ -/// Utilities for `Order` (comparison between two values). - -import Types "Types"; - -module { - - /// A type to represent an order. - public type Order = Types.Order; - - /// Check if an order is #less. - public func isLess(self : Order) : Bool { - switch self { - case (#less) { true }; - case _ { false } - } - }; - - /// Check if an order is #equal. - public func isEqual(self : Order) : Bool { - switch self { - case (#equal) { true }; - case _ { false } - } - }; - - /// Check if an order is #greater. - public func isGreater(self : Order) : Bool { - switch self { - case (#greater) { true }; - case _ { false } - } - }; - - /// Returns true if only if `order1` and `order2` are the same. - public func equal(self : Order, other : Order) : Bool { - switch (self, other) { - case (#less, #less) { true }; - case (#equal, #equal) { true }; - case (#greater, #greater) { true }; - case _ { false } - } - }; - - /// Returns an iterator that yields all possible `Order` values: - /// `#less`, `#equal`, `#greater`. - public func allValues() : Types.Iter { - var nextState : ?Order = ?#less; - { - next = func() : ?Order { - let state = nextState; - switch state { - case (?#less) { nextState := ?#equal }; - case (?#equal) { nextState := ?#greater }; - case (?#greater) { nextState := null }; - case (null) {} - }; - state - } - } - } - -} diff --git a/.mops/core@2.3.1/src/Principal.mo b/.mops/core@2.3.1/src/Principal.mo deleted file mode 100644 index d589243..0000000 --- a/.mops/core@2.3.1/src/Principal.mo +++ /dev/null @@ -1,1284 +0,0 @@ -/// Module for interacting with Principals (users and canisters). -/// -/// Principals are used to identify entities that can interact with the Internet -/// Computer. These entities are either users or canisters. -/// -/// Example textual representation of Principals: -/// -/// `un4fu-tqaaa-aaaab-qadjq-cai` -/// -/// In Motoko, there is a primitive Principal type called `Principal`. As an example -/// of where you might see Principals, you can access the Principal of the -/// caller of your shared function. -/// -/// ```motoko no-repl -/// persistent actor { -/// public shared(msg) func foo() { -/// let caller : Principal = msg.caller; -/// }; -/// } -/// ``` -/// -/// Then, you can use this module to work with the `Principal`. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Principal "mo:core/Principal"; -/// ``` - -import Prim "mo:⛔"; -import Blob "Blob"; -import Array "Array"; -import VarArray "VarArray"; -import Nat8 "Nat8"; -import Nat32 "Nat32"; -import Nat64 "Nat64"; -import Text "Text"; -import Types "Types"; - -module { - - public type Principal = Prim.Types.Principal; - - /// Get the `Principal` identifier of an actor. - /// - /// Example: - /// ```motoko include=import no-repl - /// persistent actor MyCanister { - /// func getPrincipal() : Principal { - /// let principal = Principal.fromActor(MyCanister); - /// } - /// } - /// ``` - public let fromActor : (a : actor {}) -> Principal = Prim.principalOfActor; - - /// Compute the Ledger account identifier of a principal. Optionally specify a sub-account. - /// - /// Example: - /// ```motoko include=import no-validate - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let subAccount : Blob = "\4A\8D\3F\2B\6E\01\C8\7D\9E\03\B4\56\7C\F8\9A\01\D2\34\56\78\9A\BC\DE\F0\12\34\56\78\9A\BC\DE\F0"; - /// let account = Principal.toLedgerAccount(principal, ?subAccount); - /// assert account == "\8C\5C\20\C6\15\3F\7F\51\E2\0D\0F\0F\B5\08\51\5B\47\65\63\A9\62\B4\A9\91\5F\4F\02\70\8A\ED\4F\82"; - /// ``` - public func toLedgerAccount(self : Principal, subAccount : ?Blob) : Blob { - let sha224 = SHA224(); - let accountSeparator : Blob = "\0Aaccount-id"; - sha224.writeBlob(accountSeparator); - sha224.writeBlob(toBlob(self)); - switch subAccount { - case (?subAccount) { - sha224.writeBlob(subAccount) - }; - case (null) { - let defaultSubAccount = Array.tabulate(32, func _ = 0); - sha224.writeArray(defaultSubAccount) - } - }; - - let hashSum = sha224.sum(); - - // hashBlob is a CRC32 implementation - let crc32Bytes = nat32ToByteArray(Prim.hashBlob hashSum); - - Blob.fromArray(Array.concat(crc32Bytes, Blob.toArray(hashSum))) - }; - - /// Convert a `Principal` to its `Blob` (bytes) representation. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let blob = Principal.toBlob(principal); - /// assert blob == "\00\00\00\00\00\30\00\D3\01\01"; - /// ``` - public let toBlob : (self : Principal) -> Blob = Prim.blobOfPrincipal; - - /// Converts a `Blob` (bytes) representation of a `Principal` to a `Principal` value. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\00\00\00\00\00\30\00\D3\01\01" : Blob; - /// let principal = Principal.fromBlob(blob); - /// assert Principal.toText(principal) == "un4fu-tqaaa-aaaab-qadjq-cai"; - /// ``` - public let fromBlob : (self : Blob) -> Principal = Prim.principalOfBlob; - - /// Converts a `Principal` to its `Text` representation. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.toText(principal) == "un4fu-tqaaa-aaaab-qadjq-cai"; - /// ``` - public func toText(self : Principal) : Text = debug_show (self); - - /// Converts a `Text` representation of a `Principal` to a `Principal` value. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.toText(principal) == "un4fu-tqaaa-aaaab-qadjq-cai"; - /// ``` - public func fromText(t : Text) : Principal = fromActor(actor (t)); - - private let anonymousBlob : Blob = "\04"; - - /// Constructs and returns the anonymous principal. - public func anonymous() : Principal = Prim.principalOfBlob(anonymousBlob); - - /// Checks if the given principal represents an anonymous user. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert not Principal.isAnonymous(principal); - /// ``` - public func isAnonymous(self : Principal) : Bool = Prim.blobOfPrincipal self == anonymousBlob; - - /// Checks if the given principal is a canister. - /// - /// The last byte for opaque principal ids must be 0x01 - /// https://internetcomputer.org/docs/current/references/ic-interface-spec#principal - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.isCanister(principal); - /// ``` - public func isCanister(self : Principal) : Bool { - let byteArray = toByteArray(self); - - byteArray.size() >= 0 and byteArray.size() <= 29 and isLastByte(byteArray, 1) - }; - - /// Checks if the given principal is a self authenticating principal. - /// Most of the time, this is a user principal. - /// - /// The last byte for user principal ids must be 0x02 - /// https://internetcomputer.org/docs/current/references/ic-interface-spec#principal - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("6rgy7-3uukz-jrj2k-crt3v-u2wjm-dmn3t-p26d6-ndilt-3gusv-75ybk-jae"); - /// assert Principal.isSelfAuthenticating(principal); - /// ``` - public func isSelfAuthenticating(self : Principal) : Bool { - let byteArray = toByteArray(self); - - byteArray.size() == 29 and isLastByte(byteArray, 2) - }; - - /// Checks if the given principal is a reserved principal. - /// - /// The last byte for reserved principal ids must be 0x7f - /// https://internetcomputer.org/docs/current/references/ic-interface-spec#principal - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert not Principal.isReserved(principal); - /// ``` - public func isReserved(self : Principal) : Bool { - let byteArray = toByteArray(self); - - byteArray.size() >= 0 and byteArray.size() <= 29 and isLastByte(byteArray, 127) - }; - - /// Checks if the given principal can control this canister. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert not Principal.isController(principal); - /// ``` - public func isController(self : Principal) : Bool = Prim.isController self; - - /// Hashes the given principal by hashing its `Blob` representation. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.hash(principal) == 2_742_573_646; - /// ``` - public func hash(self : Principal) : Types.Hash = Blob.hash(Prim.blobOfPrincipal(self)); - - /// General purpose comparison function for `Principal`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `principal1` with - /// `principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.compare(principal1, principal2) == #equal; - /// ``` - public func compare(self : Principal, other : Principal) : { - #less; - #equal; - #greater - } { - if (self < other) { - #less - } else if (self == other) { - #equal - } else { - #greater - } - }; - - /// Equality function for Principal types. - /// This is equivalent to `principal1 == principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.equal(principal1, principal2); - /// assert principal1 == principal2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.anonymous(); - /// let principal2 = Principal.fromBlob("\04"); - /// assert Principal.equal(principal1, principal2); - /// ``` - public func equal(self : Principal, other : Principal) : Bool { - self == other - }; - - /// Inequality function for Principal types. - /// This is equivalent to `principal1 != principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.notEqual(principal1, principal2); - /// assert not (principal1 != principal2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(self : Principal, other : Principal) : Bool { - self != other - }; - - /// "Less than" function for Principal types. - /// This is equivalent to `principal1 < principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.less(principal1, principal2); - /// assert not (principal1 < principal2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(self : Principal, other : Principal) : Bool { - self < other - }; - - /// "Less than or equal to" function for Principal types. - /// This is equivalent to `principal1 <= principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.lessOrEqual(principal1, principal2); - /// assert principal1 <= principal2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(self : Principal, other : Principal) : Bool { - self <= other - }; - - /// "Greater than" function for Principal types. - /// This is equivalent to `principal1 > principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.greater(principal1, principal2); - /// assert not (principal1 > principal2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(self : Principal, other : Principal) : Bool { - self > other - }; - - /// "Greater than or equal to" function for Principal types. - /// This is equivalent to `principal1 >= principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.greaterOrEqual(principal1, principal2); - /// assert principal1 >= principal2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(self : Principal, other : Principal) : Bool { - self >= other - }; - - /** - * SHA224 Utilities used in toAccount(). - * Utilities are not exposed as public functions. - * Taken with permission from https://github.com/research-ag/sha2 - **/ - let K00 : Nat32 = 0x428a2f98; - let K01 : Nat32 = 0x71374491; - let K02 : Nat32 = 0xb5c0fbcf; - let K03 : Nat32 = 0xe9b5dba5; - let K04 : Nat32 = 0x3956c25b; - let K05 : Nat32 = 0x59f111f1; - let K06 : Nat32 = 0x923f82a4; - let K07 : Nat32 = 0xab1c5ed5; - let K08 : Nat32 = 0xd807aa98; - let K09 : Nat32 = 0x12835b01; - let K10 : Nat32 = 0x243185be; - let K11 : Nat32 = 0x550c7dc3; - let K12 : Nat32 = 0x72be5d74; - let K13 : Nat32 = 0x80deb1fe; - let K14 : Nat32 = 0x9bdc06a7; - let K15 : Nat32 = 0xc19bf174; - let K16 : Nat32 = 0xe49b69c1; - let K17 : Nat32 = 0xefbe4786; - let K18 : Nat32 = 0x0fc19dc6; - let K19 : Nat32 = 0x240ca1cc; - let K20 : Nat32 = 0x2de92c6f; - let K21 : Nat32 = 0x4a7484aa; - let K22 : Nat32 = 0x5cb0a9dc; - let K23 : Nat32 = 0x76f988da; - let K24 : Nat32 = 0x983e5152; - let K25 : Nat32 = 0xa831c66d; - let K26 : Nat32 = 0xb00327c8; - let K27 : Nat32 = 0xbf597fc7; - let K28 : Nat32 = 0xc6e00bf3; - let K29 : Nat32 = 0xd5a79147; - let K30 : Nat32 = 0x06ca6351; - let K31 : Nat32 = 0x14292967; - let K32 : Nat32 = 0x27b70a85; - let K33 : Nat32 = 0x2e1b2138; - let K34 : Nat32 = 0x4d2c6dfc; - let K35 : Nat32 = 0x53380d13; - let K36 : Nat32 = 0x650a7354; - let K37 : Nat32 = 0x766a0abb; - let K38 : Nat32 = 0x81c2c92e; - let K39 : Nat32 = 0x92722c85; - let K40 : Nat32 = 0xa2bfe8a1; - let K41 : Nat32 = 0xa81a664b; - let K42 : Nat32 = 0xc24b8b70; - let K43 : Nat32 = 0xc76c51a3; - let K44 : Nat32 = 0xd192e819; - let K45 : Nat32 = 0xd6990624; - let K46 : Nat32 = 0xf40e3585; - let K47 : Nat32 = 0x106aa070; - let K48 : Nat32 = 0x19a4c116; - let K49 : Nat32 = 0x1e376c08; - let K50 : Nat32 = 0x2748774c; - let K51 : Nat32 = 0x34b0bcb5; - let K52 : Nat32 = 0x391c0cb3; - let K53 : Nat32 = 0x4ed8aa4a; - let K54 : Nat32 = 0x5b9cca4f; - let K55 : Nat32 = 0x682e6ff3; - let K56 : Nat32 = 0x748f82ee; - let K57 : Nat32 = 0x78a5636f; - let K58 : Nat32 = 0x84c87814; - let K59 : Nat32 = 0x8cc70208; - let K60 : Nat32 = 0x90befffa; - let K61 : Nat32 = 0xa4506ceb; - let K62 : Nat32 = 0xbef9a3f7; - let K63 : Nat32 = 0xc67178f2; - - let ivs : [[Nat32]] = [ - [ - // 224 - 0xc1059ed8, - 0x367cd507, - 0x3070dd17, - 0xf70e5939, - 0xffc00b31, - 0x68581511, - 0x64f98fa7, - 0xbefa4fa4 - ], - [ - // 256 - 0x6a09e667, - 0xbb67ae85, - 0x3c6ef372, - 0xa54ff53a, - 0x510e527f, - 0x9b05688c, - 0x1f83d9ab, - 0x5be0cd19 - ] - ]; - - let rot = Nat32.bitrotRight; - - class SHA224() { - let (sum_bytes, iv) = (28, 0); - - var s0 : Nat32 = 0; - var s1 : Nat32 = 0; - var s2 : Nat32 = 0; - var s3 : Nat32 = 0; - var s4 : Nat32 = 0; - var s5 : Nat32 = 0; - var s6 : Nat32 = 0; - var s7 : Nat32 = 0; - - let msg : [var Nat32] = VarArray.repeat(0, 16); - let digest = VarArray.repeat(0, sum_bytes); - var word : Nat32 = 0; - - var i_msg : Nat8 = 0; - var i_byte : Nat8 = 4; - var i_block : Nat64 = 0; - - public func reset() { - i_msg := 0; - i_byte := 4; - i_block := 0; - s0 := ivs[iv][0]; - s1 := ivs[iv][1]; - s2 := ivs[iv][2]; - s3 := ivs[iv][3]; - s4 := ivs[iv][4]; - s5 := ivs[iv][5]; - s6 := ivs[iv][6]; - s7 := ivs[iv][7] - }; - - reset(); - - private func writeByte(val : Nat8) : () { - word := (word << 8) ^ Nat32.fromIntWrap(Nat8.toNat(val)); - i_byte -%= 1; - if (i_byte == 0) { - msg[Nat8.toNat(i_msg)] := word; - word := 0; - i_byte := 4; - i_msg +%= 1; - if (i_msg == 16) { - process_block(); - i_msg := 0; - i_block +%= 1 - } - } - }; - - private func process_block() : () { - let w00 = msg[0]; - let w01 = msg[1]; - let w02 = msg[2]; - let w03 = msg[3]; - let w04 = msg[4]; - let w05 = msg[5]; - let w06 = msg[6]; - let w07 = msg[7]; - let w08 = msg[8]; - let w09 = msg[9]; - let w10 = msg[10]; - let w11 = msg[11]; - let w12 = msg[12]; - let w13 = msg[13]; - let w14 = msg[14]; - let w15 = msg[15]; - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - /* - for ((i, j, k, l, m) in expansion_rounds.values()) { - // (j,k,l,m) = (i+1,i+9,i+14,i+16) - let (v0, v1) = (msg[j], msg[l]); - let s0 = rot(v0, 07) ^ rot(v0, 18) ^ (v0 >> 03); - let s1 = rot(v1, 17) ^ rot(v1, 19) ^ (v1 >> 10); - msg[m] := msg[i] +% s0 +% msg[k] +% s1; - }; - */ - // compress - var a = s0; - var b = s1; - var c = s2; - var d = s3; - var e = s4; - var f = s5; - var g = s6; - var h = s7; - var t = 0 : Nat32; - - t := h +% K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K09 +% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K10 +% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K11 +% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K12 +% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K13 +% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K14 +% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - - /* - for (i in compression_rounds.keys()) { - let ch = (e & f) ^ (^ e & g); - let maj = (a & b) ^ (a & c) ^ (b & c); - let sigma0 = rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - let sigma1 = rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - let t = h +% K[i] +% msg[i] +% ch +% sigma1; - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% maj +% sigma0; - }; - */ - // final addition - s0 +%= a; - s1 +%= b; - s2 +%= c; - s3 +%= d; - s4 +%= e; - s5 +%= f; - s6 +%= g; - s7 +%= h - }; - - public func writeIter(iter : { next() : ?Nat8 }) : () { - label reading loop { - switch (iter.next()) { - case (?val) { - writeByte(val); - continue reading - }; - case (null) { - break reading - } - } - } - }; - - public func writeArray(arr : [Nat8]) : () = writeIter(arr.vals()); - public func writeBlob(blob : Blob) : () = writeIter(blob.vals()); - - public func sum() : Blob { - // calculate padding - // t = bytes in the last incomplete block (0-63) - let t : Nat8 = (i_msg << 2) +% 4 -% i_byte; - // p = length of padding (1-64) - var p : Nat8 = if (t < 56) (56 -% t) else (120 -% t); - // n_bits = length of message in bits - let n_bits : Nat64 = ((i_block << 6) +% Nat64.fromIntWrap(Nat8.toNat(t))) << 3; - - // write padding - writeByte(0x80); - p -%= 1; - while (p != 0) { - writeByte(0x00); - p -%= 1 - }; - - // write length (8 bytes) - // Note: this exactly fills the block buffer, hence process_block will get - // triggered by the last writeByte - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 56) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 48) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 40) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 32) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 24) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 16) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 8) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat(n_bits & 0xff))); - - // retrieve sum - digest[0] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 24) & 0xff)); - digest[1] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 16) & 0xff)); - digest[2] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 8) & 0xff)); - digest[3] := Nat8.fromIntWrap(Nat32.toNat(s0 & 0xff)); - digest[4] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 24) & 0xff)); - digest[5] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 16) & 0xff)); - digest[6] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 8) & 0xff)); - digest[7] := Nat8.fromIntWrap(Nat32.toNat(s1 & 0xff)); - digest[8] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 24) & 0xff)); - digest[9] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 16) & 0xff)); - digest[10] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 8) & 0xff)); - digest[11] := Nat8.fromIntWrap(Nat32.toNat(s2 & 0xff)); - digest[12] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 24) & 0xff)); - digest[13] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 16) & 0xff)); - digest[14] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 8) & 0xff)); - digest[15] := Nat8.fromIntWrap(Nat32.toNat(s3 & 0xff)); - digest[16] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 24) & 0xff)); - digest[17] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 16) & 0xff)); - digest[18] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 8) & 0xff)); - digest[19] := Nat8.fromIntWrap(Nat32.toNat(s4 & 0xff)); - digest[20] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 24) & 0xff)); - digest[21] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 16) & 0xff)); - digest[22] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 8) & 0xff)); - digest[23] := Nat8.fromIntWrap(Nat32.toNat(s5 & 0xff)); - digest[24] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 24) & 0xff)); - digest[25] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 16) & 0xff)); - digest[26] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 8) & 0xff)); - digest[27] := Nat8.fromIntWrap(Nat32.toNat(s6 & 0xff)); - - return Blob.fromVarArray(digest) - } - }; // class SHA224 - - func nat32ToByteArray(n : Nat32) : [Nat8] { - func byte(n : Nat32) : Nat8 { - Nat8.fromNat(Nat32.toNat(n & 0xff)) - }; - [byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)] - }; - - func toByteArray(p : Principal) : [Nat8] = Blob.toArray(toBlob(p)); - - func isLastByte(byteArray : [Nat8], byte : Nat8) : Bool { - let size = byteArray.size(); - size > 0 and byteArray[size - 1] == byte - } -} diff --git a/.mops/core@2.3.1/src/PriorityQueue.mo b/.mops/core@2.3.1/src/PriorityQueue.mo deleted file mode 100644 index 4045b4f..0000000 --- a/.mops/core@2.3.1/src/PriorityQueue.mo +++ /dev/null @@ -1,299 +0,0 @@ -/// A mutable priority queue of elements. -/// Always returns the element with the highest priority first, -/// as determined by a user-provided comparison function. -/// -/// Typical use cases include: -/// * Task scheduling (highest-priority task first) -/// * Event simulation -/// * Pathfinding algorithms (e.g. Dijkstra, A*) -/// -/// Example: -/// ```motoko -/// import PriorityQueue "mo:core/PriorityQueue"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// let pq = PriorityQueue.empty(); -/// PriorityQueue.push(pq, Nat.compare, 5); -/// PriorityQueue.push(pq, Nat.compare, 10); -/// PriorityQueue.push(pq, Nat.compare, 3); -/// assert PriorityQueue.pop(pq, Nat.compare) == ?10; -/// assert PriorityQueue.pop(pq, Nat.compare) == ?5; -/// assert PriorityQueue.pop(pq, Nat.compare) == ?3; -/// assert PriorityQueue.pop(pq, Nat.compare) == null; -/// } -/// ``` -/// -/// Internally implemented as a binary heap stored in a core library `List`. -/// -/// Performance: -/// * Runtime: `O(log n)` for `push` and `pop` (amortized). -/// * Runtime: `O(1)` for `peek`, `clear`, `size`, and `isEmpty`. -/// * Space: `O(n)`, where `n` is the number of stored elements. -/// -/// Implementation note (due to `List`): -/// * There is an additive memory overhead of `O(sqrt(n))`. -/// * For `push` and `pop`, the amortized time is `O(log n)`, -/// but the worst case can involve an extra `O(sqrt(n))` step. -import List "List"; -import Types "Types"; -import Order "Order"; - -module { - public type PriorityQueue = Types.PriorityQueue; - - /// Returns an empty priority queue. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// - /// let pq = PriorityQueue.empty(); - /// assert PriorityQueue.isEmpty(pq); - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func empty() : PriorityQueue = { - heap = List.empty() - }; - - /// Returns a priority queue containing a single element. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// - /// let pq = PriorityQueue.singleton(42); - /// assert PriorityQueue.peek(pq) == ?42; - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func singleton(element : T) : PriorityQueue = { - heap = List.singleton(element) - }; - - /// Returns the number of elements in the priority queue. - /// - /// Runtime: `O(1)`. - public func size(self : PriorityQueue) : Nat = List.size(self.heap); - - /// Returns `true` iff the priority queue is empty. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let pq = PriorityQueue.empty(); - /// assert PriorityQueue.isEmpty(pq); - /// PriorityQueue.push(pq, Nat.compare, 5); - /// assert not PriorityQueue.isEmpty(pq); - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func isEmpty(self : PriorityQueue) : Bool = List.isEmpty(self.heap); - - /// Removes all elements from the priority queue. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// - /// let pq = PriorityQueue.empty(); - /// PriorityQueue.push(pq, Nat.compare, 5); - /// PriorityQueue.push(pq, Nat.compare, 10); - /// assert not PriorityQueue.isEmpty(pq); - /// PriorityQueue.clear(pq); - /// assert PriorityQueue.isEmpty(pq); - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func clear(self : PriorityQueue) = List.clear(self.heap); - - /// Inserts a new element into the priority queue. - /// - /// `compare` – comparison function that defines priority ordering. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let pq = PriorityQueue.empty(); - /// PriorityQueue.push(pq, Nat.compare, 5); - /// PriorityQueue.push(pq, Nat.compare, 10); - /// assert PriorityQueue.peek(pq) == ?10; - /// ``` - /// - /// Runtime: `O(log n)`. Space: `O(1)`. - public func push( - self : PriorityQueue, - compare : (implicit : (T, T) -> Order.Order), - element : T - ) { - let heap = self.heap; - List.add(heap, element); - var index : Nat = List.size(heap) - 1; - while (index > 0) { - let parentId = (index - 1) : Nat / 2; - let parentVal = List.at(heap, parentId); - if (compare(element, parentVal) == #greater) { - List.put(heap, index, parentVal); - index := parentId - } else { - List.put(heap, index, element); - return - } - }; - List.put(heap, 0, element) - }; - - /// Returns the element with the highest priority, without removing it. - /// Returns `null` if the queue is empty. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// - /// let pq = PriorityQueue.singleton(42); - /// assert PriorityQueue.peek(pq) == ?42; - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func peek(self : PriorityQueue) : ?T = List.get(self.heap, 0); - - /// Removes and returns the element with the highest priority. - /// Returns `null` if the queue is empty. - /// - /// `compare` – comparison function that defines priority ordering. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let pq = PriorityQueue.empty(); - /// PriorityQueue.push(pq, Nat.compare, 5); - /// PriorityQueue.push(pq, Nat.compare, 10); - /// assert PriorityQueue.pop(pq, Nat.compare) == ?10; - /// ``` - /// - /// Runtime: `O(log n)`. Space: `O(1)`. - public func pop( - self : PriorityQueue, - compare : (implicit : (T, T) -> Order.Order) - ) : ?T { - let heap = self.heap; - if (List.isEmpty(heap)) { - return null - }; - let top = List.get(heap, 0); - let lastIndex : Nat = List.size(heap) - 1; - let lastElem = List.at(heap, lastIndex); - - var index = 0; - loop { - var best = lastIndex; - let left = 2 * index + 1; - var bestElem = lastElem; - if (left < lastIndex) { - let leftElem = List.at(heap, left); - if (compare(leftElem, lastElem) == #greater) { - best := left; - bestElem := leftElem - } - }; - let right = left + 1; - if (right < lastIndex) { - let rightElem = List.at(heap, right); - if (compare(rightElem, bestElem) == #greater) { - best := right; - bestElem := rightElem - } - }; - if (best == lastIndex) { - List.put(heap, index, lastElem); - ignore List.removeLast(heap); - return top - }; - List.put(heap, index, bestElem); - index := best - } - }; - - /// Creates a new priority queue from an iterator. - /// - /// `compare` – comparison function that defines priority ordering. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let pq = PriorityQueue.fromIter([5, 10, 3].values(), Nat.compare); - /// assert PriorityQueue.size(pq) == 3; - /// assert PriorityQueue.peek(pq) == ?10; - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// `n` denotes the number of elements in the iterator. - public func fromIter(iter : Types.Iter, compare : (implicit : (T, T) -> Order.Order)) : PriorityQueue { - let pq = empty(); - for (element in iter) { - push(pq, element) - }; - pq - }; - - /// Creates a copy of the priority queue. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let original = PriorityQueue.fromIter([5, 10, 3].values(), Nat.compare); - /// let copy = PriorityQueue.clone(original); - /// assert PriorityQueue.pop(copy, Nat.compare) == ?10; - /// assert PriorityQueue.size(original) == 3; - /// ``` - /// - /// Runtime: `O(n)`. Space: `O(n)`. - /// `n` denotes the number of elements in the priority queue. - public func clone(self : PriorityQueue) : PriorityQueue = { - heap = List.clone(self.heap) - }; - - /// Returns an iterator that yields elements in descending priority order - /// (highest priority first, matching `pop` semantics). - /// - /// The original queue is not modified. Internally clones the heap - /// and pops from the clone on each `next()` call. - /// - /// `compare` – comparison function that defines priority ordering. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let pq = PriorityQueue.fromIter([5, 10, 3].values(), Nat.compare); - /// assert Iter.toArray(PriorityQueue.values(pq, Nat.compare)) == [10, 5, 3]; - /// ``` - /// - /// Runtime: `O(n)` to create the iterator, `O(log n)` per `next()` call. - /// Space: `O(n)` for the internal clone. - /// `n` denotes the number of elements in the priority queue. - public func values(self : PriorityQueue, compare : (implicit : (T, T) -> Order.Order)) : Types.Iter { - let copy : PriorityQueue = clone(self); - object { - public func next() : ?T { - pop(copy) - } - } - } -} diff --git a/.mops/core@2.3.1/src/Queue.mo b/.mops/core@2.3.1/src/Queue.mo deleted file mode 100644 index d8f48c5..0000000 --- a/.mops/core@2.3.1/src/Queue.mo +++ /dev/null @@ -1,820 +0,0 @@ -/// A mutable double-ended queue of elements. -/// The queue has two ends, front and back. -/// Elements can be added and removed at the two ends. -/// -/// This can be used for different use cases, such as: -/// * Queue (FIFO) by using `pushBack()` and `popFront()` -/// * Stack (LIFO) by using `pushFront()` and `popFront()`. -/// -/// Example: -/// ```motoko -/// import Queue "mo:core/Queue"; -/// -/// persistent actor { -/// let orders = Queue.empty(); -/// Queue.pushBack(orders, "Motoko"); -/// Queue.pushBack(orders, "Mops"); -/// Queue.pushBack(orders, "IC"); -/// assert Queue.popFront(orders) == ?"Motoko"; -/// assert Queue.popFront(orders) == ?"Mops"; -/// assert Queue.popFront(orders) == ?"IC"; -/// assert Queue.popFront(orders) == null; -/// } -/// ``` -/// -/// The internal implementation is a doubly-linked list. -/// -/// Performance: -/// * Runtime: `O(1)` for push, pop, and peek operations. -/// * Space: `O(n)`. -/// `n` denotes the number of elements stored in the queue. - -import PureQueue "pure/Queue"; -import Iter "Iter"; -import Order "Order"; -import Types "Types"; -import Array "Array"; -import Prim "mo:⛔"; - -module { - public type Queue = Types.Queue.Queue; - - type Node = Types.Queue.Node; - - /// Converts a mutable queue to an immutable, purely functional queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let pureQueue = Queue.toPure(queue); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - /// @deprecated M0235 - public func toPure(self : Queue) : PureQueue.Queue { - let pureQueue = PureQueue.empty(); - let iter = values(self); - var current = pureQueue; - loop { - switch (iter.next()) { - case null { return current }; - case (?val) { current := PureQueue.pushBack(current, val) } - } - } - }; - - /// Converts an immutable, purely functional queue to a mutable queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import PureQueue "mo:core/pure/Queue"; - /// - /// persistent actor { - /// let pureQueue = PureQueue.fromIter([1, 2, 3].values()); - /// let queue = Queue.fromPure(pureQueue); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - /// @deprecated M0235 - public func fromPure(pureQueue : PureQueue.Queue) : Queue { - let queue = empty(); - let iter = PureQueue.values(pureQueue); - loop { - switch (iter.next()) { - case null { return queue }; - case (?val) { pushBack(queue, val) } - } - } - }; - - /// Create a new empty mutable double-ended queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.size(queue) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Queue { - { var front = null; var back = null; var size = 0 } - }; - - /// Creates a new queue with a single element. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.singleton(123); - /// assert Queue.size(queue) == 1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func singleton(element : T) : Queue { - let queue = empty(); - pushBack(queue, element); - queue - }; - - /// Removes all elements from the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// Queue.clear(queue); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func clear(self : Queue) { - self.front := null; - self.back := null; - self.size := 0 - }; - - /// Creates a deep copy of the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let original = Queue.fromIter([1, 2, 3].values()); - /// let copy = Queue.clone(original); - /// Queue.clear(original); - /// assert Queue.size(original) == 0; - /// assert Queue.size(copy) == 3; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func clone(self : Queue) : Queue { - let copy = empty(); - for (element in values(self)) { - pushBack(copy, element) - }; - copy - }; - - /// Returns the number of elements in the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter(["A", "B", "C"].values()); - /// assert Queue.size(queue) == 3; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func size(self : Queue) : Nat { - self.size - }; - - /// Returns `true` if the queue contains no elements. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func isEmpty(self : Queue) : Bool { - self.size == 0 - }; - - /// Checks if an element exists in the queue using the provided equality function. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.contains(queue, Nat.equal, 2); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func contains(self : Queue, equal : (implicit : (T, T) -> Bool), element : T) : Bool { - for (existing in values(self)) { - if (equal(existing, element)) { - return true - } - }; - false - }; - - /// Returns the first element in the queue without removing it. - /// Returns null if the queue is empty. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.peekFront(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func peekFront(self : Queue) : ?T { - switch (self.front) { - case null null; - case (?node) ?node.value - } - }; - - /// Returns the last element in the queue without removing it. - /// Returns null if the queue is empty. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.peekBack(queue) == ?3; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func peekBack(self : Queue) : ?T { - switch (self.back) { - case null null; - case (?node) ?node.value - } - }; - - /// Adds an element to the front of the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.empty(); - /// Queue.pushFront(queue, 1); - /// assert Queue.peekFront(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func pushFront(self : Queue, element : T) { - let node : Node = { - value = element; - var next = self.front; - var previous = null - }; - switch (self.front) { - case null {}; - case (?first) first.previous := ?node - }; - self.front := ?node; - switch (self.back) { - case null self.back := ?node; - case (?_) {} - }; - self.size += 1 - }; - - /// Adds an element to the back of the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.empty(); - /// Queue.pushBack(queue, 1); - /// assert Queue.peekBack(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func pushBack(self : Queue, element : T) { - let node : Node = { - value = element; - var next = null; - var previous = self.back - }; - switch (self.back) { - case null {}; - case (?last) last.next := ?node - }; - self.back := ?node; - switch (self.front) { - case null self.front := ?node; - case (?_) {} - }; - self.size += 1 - }; - - /// Removes and returns the first element in the queue. - /// Returns null if the queue is empty. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.popFront(queue) == ?1; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func popFront(self : Queue) : ?T { - switch (self.front) { - case null null; - case (?first) { - self.front := first.next; - switch (self.front) { - case null { self.back := null }; - case (?newFirst) { newFirst.previous := null } - }; - self.size -= 1; - ?first.value - } - } - }; - - /// Removes and returns the last element in the queue. - /// Returns null if the queue is empty. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.popBack(queue) == ?3; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func popBack(self : Queue) : ?T { - switch (self.back) { - case null null; - case (?last) { - self.back := last.previous; - switch (self.back) { - case null { self.front := null }; - case (?newLast) { newLast.next := null } - }; - self.size -= 1; - ?last.value - } - } - }; - - /// Creates a new queue from an iterator. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter(["A", "B", "C"].values()); - /// assert Queue.size(queue) == 3; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func fromIter(iter : Iter.Iter) : Queue { - let queue = empty(); - for (element in iter) { - pushBack(queue, element) - }; - queue - }; - - /// Converts an iterator to a queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// transient let iter = ["A", "B", "C"].values(); - /// - /// let queue = iter.toQueue(); - /// - /// assert Queue.size(queue) == 3; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func toQueue(self : Iter.Iter) : Queue { - fromIter(self) - }; - - /// Creates a new queue from an array. - /// Elements appear in the same order as in the array. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromArray(["A", "B", "C"]); - /// assert Queue.size(queue) == 3; - /// assert Queue.peekFront(queue) == ?"A"; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the array. - public func fromArray(array : [T]) : Queue { - let queue = empty(); - for (element in array.vals()) { - pushBack(queue, element) - }; - queue - }; - - public func fromVarArray(array : [var T]) : Queue { - fromIter(array.values()) - }; - - /// Creates a new immutable array containing all elements from the queue. - /// Elements appear in the same order as in the queue (front to back). - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Array "mo:core/Array"; - /// - /// persistent actor { - /// let queue = Queue.fromArray(["A", "B", "C"]); - /// let array = Queue.toArray(queue); - /// assert array == ["A", "B", "C"]; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func toArray(self : Queue) : [T] { - let iter = values(self); - Array.tabulate( - self.size, - func(i) { - switch (iter.next()) { - case null { Prim.trap("Queue.toArray(): unexpected end of iterator") }; - case (?value) { value } - } - } - ) - }; - - public func toVarArray(self : Queue) : [var T] { - Array.toVarArray(toArray(self)) - }; - - /// Returns an iterator over the elements in the queue. - /// Iterates from front to back. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// persistent actor { - /// let queue = Queue.fromIter(["A", "B", "C"].values()); - /// transient let iter = Queue.values(queue); - /// assert iter.next() == ?"A"; - /// assert iter.next() == ?"B"; - /// assert iter.next() == ?"C"; - /// assert iter.next() == null; - /// } - /// ``` - /// - /// Runtime: O(1) for iterator creation, O(n) for full iteration - /// Space: O(1) - public func values(self : Queue) : Iter.Iter { - object { - var current = self.front; - - public func next() : ?T { - switch (current) { - case null null; - case (?node) { - current := node.next; - ?node.value - } - } - } - } - }; - - public func reverseValues(self : Queue) : Iter.Iter { - Iter.reverse(values(self)) - }; - - /// Tests whether all elements in the queue satisfy the given predicate. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([2, 4, 6].values()); - /// assert Queue.all(queue, func(x) { x % 2 == 0 }); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - public func all(self : Queue, predicate : T -> Bool) : Bool { - for (element in values(self)) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Tests whether any element in the queue satisfies the given predicate. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.any(queue, func (x) { x > 2 }); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func any(self : Queue, predicate : T -> Bool) : Bool { - for (element in values(self)) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Applies the given operation to all elements in the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// var sum = 0; - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// Queue.forEach(queue, func(x) { sum += x }); - /// assert sum == 6; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func forEach(self : Queue, operation : T -> ()) { - for (element in values(self)) { - operation(element) - } - }; - - /// Creates a new queue by applying the given function to all elements. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let doubled = Queue.map(queue, func(x) { x * 2 }); - /// assert Queue.peekFront(doubled) == ?2; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func map(self : Queue, project : T -> U) : Queue { - let result = empty(); - for (element in values(self)) { - pushBack(result, project(element)) - }; - result - }; - - /// Creates a new queue containing only elements that satisfy the given predicate. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3, 4].values()); - /// let evens = Queue.filter(queue, func(x) { x % 2 == 0 }); - /// assert Queue.size(evens) == 2; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func filter(self : Queue, criterion : T -> Bool) : Queue { - let result = empty(); - for (element in values(self)) { - if (criterion(element)) { - pushBack(result, element) - } - }; - result - }; - - /// Creates a new queue by applying the given function to all elements - /// and keeping only the non-null results. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3, 4].values()); - /// let evenDoubled = Queue.filterMap( - /// queue, - /// func(x) { - /// if (x % 2 == 0) { ?(x * 2) } else { null } - /// } - /// ); - /// assert Queue.size(evenDoubled) == 2; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func filterMap(self : Queue, project : T -> ?U) : Queue { - let result = empty(); - for (element in values(self)) { - switch (project(element)) { - case null {}; - case (?newElement) pushBack(result, newElement) - } - }; - result - }; - - /// Compares two queues for equality using the provided equality function. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2, 3].values()); - /// let queue2 = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.equal(queue1, queue2, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func equal(self : Queue, other : Queue, equal : (implicit : (T, T) -> Bool)) : Bool { - if (size(self) != size(other)) { - return false - }; - let iterator1 = values(self); - let iterator2 = values(other); - loop { - let element1 = iterator1.next(); - let element2 = iterator2.next(); - switch (element1, element2) { - case (null, null) { - return true - }; - case (?element1, ?element2) { - if (not equal(element1, element2)) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Converts a queue to its string representation using the provided element formatter. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.toText(queue, Nat.toText) == "Queue[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func toText(self : Queue, format : (implicit : (toText : T -> Text))) : Text { - var text = "Queue["; - var sep = ""; - for (element in values(self)) { - text #= sep # format(element); - sep := ", " - }; - text #= "]"; - text - }; - - /// Compares two queues using the provided comparison function. - /// Returns #less, #equal, or #greater. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2].values()); - /// let queue2 = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.compare(queue1, queue2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func compare(self : Queue, other : Queue, compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let iterator1 = values(self); - let iterator2 = values(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?element1, ?element2) { - let comparison = compare(element1, element2); - if (comparison != #equal) { - return comparison - } - } - } - } - } -} diff --git a/.mops/core@2.3.1/src/Random.mo b/.mops/core@2.3.1/src/Random.mo deleted file mode 100644 index 4283653..0000000 --- a/.mops/core@2.3.1/src/Random.mo +++ /dev/null @@ -1,456 +0,0 @@ -/// Random number generation. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Random "mo:core/Random"; -/// ``` - -import Nat8 "Nat8"; -import Nat64 "Nat64"; -import Int "Int"; -import Nat "Nat"; -import Blob "Blob"; -import Runtime "Runtime"; - -module { - - public type State = { - var bytes : [Nat8]; - var index : Nat; - var bits : Nat8; - var bitMask : Nat8 - }; - - public type SeedState = { - random : State; - prng : PRNG.State - }; - - let rawRand = (actor "aaaaa-aa" : actor { raw_rand : () -> async Blob }).raw_rand; - - public let blob : shared () -> async Blob = rawRand; - - public func bool() : async Bool { - await* crypto().bool() - }; - public func nat8() : async Nat8 { - await* crypto().nat8() - }; - public func nat64() : async Nat64 { - await* crypto().nat64() - }; - public func nat64Range(fromInclusive : Nat64, toExclusive : Nat64) : async Nat64 { - await* crypto().nat64Range(fromInclusive, toExclusive) - }; - public func natRange(fromInclusive : Nat, toExclusive : Nat) : async Nat { - await* crypto().natRange(fromInclusive, toExclusive) - }; - public func intRange(fromInclusive : Int, toExclusive : Int) : async Int { - await* crypto().intRange(fromInclusive, toExclusive) - }; - - /// Initializes a random number generator state. This is used - /// to create a `Random` or `AsyncRandom` instance with a specific state. - /// The state is empty, but it can be reused after upgrading the canister. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// let state = Random.emptyState(); - /// transient let random = Random.cryptoFromState(state); - /// - /// public func main() : async () { - /// let coin = await* random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func emptyState() : State = { - var bytes = []; - var index = 0; - var bits = 0x00; - var bitMask = 0x00 - }; - - /// Initializes a pseudo-random number generator state with a 64-bit seed. - /// This is used to create a `Random` instance with a specific seed. - /// The seed is used to initialize the PRNG state. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// let state = Random.seedState(123); - /// transient let random = Random.seedFromState(state); - /// - /// public func main() : async () { - /// let coin = random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func seedState(seed : Nat64) : SeedState = { - random = emptyState(); - prng = PRNG.init(seed) - }; - - /// Creates a pseudo-random number generator from a 64-bit seed. - /// The seed is used to initialize the PRNG state. - /// This is suitable for simulations and testing, but not for cryptographic purposes. - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(123); - /// let coin = random.bool(); // true or false - /// ``` - /// @deprecated M0235 - public func seed(seed : Nat64) : Random { - seedFromState(seedState(seed)) - }; - - /// Creates a pseudo-random number generator with the given state. - /// This provides statistical randomness suitable for simulations and testing, - /// but should not be used for cryptographic purposes. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// let state = Random.seedState(123); - /// transient let random = Random.seedFromState(state); - /// - /// public func main() : async () { - /// let coin = random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func seedFromState(state : SeedState) : Random { - Random( - state.random, - func() : Blob { - // Generate 8 bytes directly from a single 64-bit number - let n = PRNG.next(state.prng); - let (b7, b6, b5, b4, b3, b2, b1, b0) = Nat64.explode(n); - Blob.fromArray([b0, b1, b2, b3, b4, b5, b6, b7]) - } - ) - }; - - /// Initializes a cryptographic random number generator - /// using entropy from the ICP management canister. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// transient let random = Random.crypto(); - /// - /// public func main() : async () { - /// let coin = await* random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func crypto() : AsyncRandom { - cryptoFromState(emptyState()) - }; - - /// Creates a random number generator suitable for cryptography - /// using entropy from the ICP management canister. Initializing - /// from a state makes it possible to reuse entropy after - /// upgrading the canister. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// let state = Random.emptyState(); - /// transient let random = Random.cryptoFromState(state); - /// - /// func example() : async () { - /// let coin = await* random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func cryptoFromState(state : State) : AsyncRandom { - AsyncRandom(state, func() : async* Blob { await rawRand() }) - }; - - /// @deprecated M0235 - public class Random(state : State, generator : () -> Blob) { - - func nextBit() : Bool { - if (0 : Nat8 == state.bitMask) { - state.bits := nat8(); - state.bitMask := 0x40; - 0 : Nat8 != state.bits & (0x80 : Nat8) - } else { - let m = state.bitMask; - state.bitMask >>= (1 : Nat8); - 0 : Nat8 != state.bits & m - } - }; - - /// Random choice between `true` and `false`. - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let coin = random.bool(); // true or false - /// ``` - /// @deprecated M0235 - public func bool() : Bool { - nextBit() - }; - - /// Random `Nat8` value in the range [0, 256). - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let byte = random.nat8(); // 0 to 255 - /// ``` - /// @deprecated M0235 - public func nat8() : Nat8 { - if (state.index >= state.bytes.size()) { - let newBytes = Blob.toArray(generator()); - if (newBytes.size() == 0) { - Runtime.trap("Random: generator produced empty Blob") - }; - state.bytes := newBytes; - state.index := 0 - }; - let byte = state.bytes[state.index]; - state.index += 1; - byte - }; - - // Helper function which returns a uniformly sampled `Nat64` in the range `[0, max]`. - // Uses rejection sampling to ensure uniform distribution even when the range - // doesn't divide evenly into 2^64. This avoids modulo bias that would occur - // from simply taking the modulo of a random 64-bit number. - func uniform64(max : Nat64) : Nat64 { - if (max == 0) { - return 0 - }; - // if (max == 1) { - // return switch (bool()) { - // case false 0; - // case true 1 - // } - // }; - if (max == Nat64.maxValue) { - return nat64() - }; - let toExclusive = max + 1; - // 2^64 - (2^64 % toExclusive) = (2^64-1) - (2^64-1 % toExclusive): - let cutoff = Nat64.maxValue - (Nat64.maxValue % toExclusive); - // 2^64 / toExclusive, with toExclusive > 1: - let multiple = Nat64.fromNat(/* 2^64 */ 0x10000000000000000 / Nat64.toNat(toExclusive)); - loop { - // Build up a random Nat64 from bytes - var number = nat64(); - // If number is below cutoff, we can use it - if (number < cutoff) { - // Scale down to desired range - return number / multiple - }; - // Otherwise reject and try again - } - }; - - /// Random `Nat64` value in the range [0, 2^64). - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let number = random.nat64(); // 0 to 18446744073709551615 - /// ``` - /// @deprecated M0235 - public func nat64() : Nat64 { - (Nat64.fromNat(Nat8.toNat(nat8())) << 56) | (Nat64.fromNat(Nat8.toNat(nat8())) << 48) | (Nat64.fromNat(Nat8.toNat(nat8())) << 40) | (Nat64.fromNat(Nat8.toNat(nat8())) << 32) | (Nat64.fromNat(Nat8.toNat(nat8())) << 24) | (Nat64.fromNat(Nat8.toNat(nat8())) << 16) | (Nat64.fromNat(Nat8.toNat(nat8())) << 8) | Nat64.fromNat(Nat8.toNat(nat8())) - }; - - /// Random `Nat64` value in the range [fromInclusive, toExclusive). - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let dice = random.nat64Range(1, 7); // 1 to 6 - /// ``` - /// @deprecated M0235 - public func nat64Range(fromInclusive : Nat64, toExclusive : Nat64) : Nat64 { - if (fromInclusive >= toExclusive) { - Runtime.trap("Random.nat64Range(): fromInclusive >= toExclusive") - }; - uniform64(toExclusive - fromInclusive - 1) + fromInclusive - }; - - /// Random `Nat` value in the range [fromInclusive, toExclusive). - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let index = random.natRange(0, 10); // 0 to 9 - /// ``` - /// @deprecated M0235 - public func natRange(fromInclusive : Nat, toExclusive : Nat) : Nat { - if (fromInclusive >= toExclusive) { - Runtime.trap("Random.natRange(): fromInclusive >= toExclusive") - }; - Nat64.toNat(uniform64(Nat64.fromNat(toExclusive - fromInclusive - 1))) + fromInclusive - }; - - /// @deprecated M0235 - public func intRange(fromInclusive : Int, toExclusive : Int) : Int { - let range = Nat.fromInt(toExclusive - fromInclusive - 1); - Nat64.toNat(uniform64(Nat64.fromNat(range))) + fromInclusive - }; - - }; - - /// @deprecated M0235 - public class AsyncRandom(state : State, generator : () -> async* Blob) { - - func nextBit() : async* Bool { - if (0 : Nat8 == state.bitMask) { - state.bits := await* nat8(); - state.bitMask := 0x40; - 0 : Nat8 != state.bits & (0x80 : Nat8) - } else { - let m = state.bitMask; - state.bitMask >>= (1 : Nat8); - 0 : Nat8 != state.bits & m - } - }; - - /// Random choice between `true` and `false`. - /// @deprecated M0235 - public func bool() : async* Bool { - await* nextBit() - }; - - /// Random `Nat8` value in the range [0, 256). - /// @deprecated M0235 - public func nat8() : async* Nat8 { - if (state.index >= state.bytes.size()) { - let newBytes = Blob.toArray(await* generator()); - if (newBytes.size() == 0) { - Runtime.trap("AsyncRandom: generator produced empty Blob") - }; - state.bytes := newBytes; - state.index := 0 - }; - let byte = state.bytes[state.index]; - state.index += 1; - byte - }; - - // Helper function which returns a uniformly sampled `Nat64` in the range `[0, max]`. - // Uses rejection sampling to ensure uniform distribution even when the range - // doesn't divide evenly into 2^64. This avoids modulo bias that would occur - // from simply taking the modulo of a random 64-bit number. - func uniform64(max : Nat64) : async* Nat64 { - if (max == 0) { - return 0 - }; - if (max == Nat64.maxValue) { - return await* nat64() - }; - let toExclusive = max + 1; - // 2^64 - (2^64 % toExclusive) = (2^64-1) - (2^64-1 % toExclusive): - let cutoff = Nat64.maxValue - (Nat64.maxValue % toExclusive); - // 2^64 / toExclusive, with toExclusive > 1: - let multiple = Nat64.fromNat(/* 2^64 */ 0x10000000000000000 / Nat64.toNat(toExclusive)); - loop { - // Build up a random Nat64 from bytes - var number = await* nat64(); - // If number is below cutoff, we can use it - if (number < cutoff) { - // Scale down to desired range - return number / multiple - }; - // Otherwise reject and try again - } - }; - - /// Random `Nat64` value in the range [0, 2^64). - /// @deprecated M0235 - public func nat64() : async* Nat64 { - (Nat64.fromNat(Nat8.toNat(await* nat8())) << 56) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 48) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 40) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 32) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 24) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 16) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 8) | Nat64.fromNat(Nat8.toNat(await* nat8())) - }; - - /// Random `Nat64` value in the range [fromInclusive, toExclusive). - /// @deprecated M0235 - public func nat64Range(fromInclusive : Nat64, toExclusive : Nat64) : async* Nat64 { - if (fromInclusive >= toExclusive) { - Runtime.trap("AsyncRandom.nat64Range(): fromInclusive >= toExclusive") - }; - (await* uniform64(toExclusive - fromInclusive - 1)) + fromInclusive - }; - - /// Random `Nat` value in the range [fromInclusive, toExclusive). - /// @deprecated M0235 - public func natRange(fromInclusive : Nat, toExclusive : Nat) : async* Nat { - if (fromInclusive >= toExclusive) { - Runtime.trap("AsyncRandom.natRange(): fromInclusive >= toExclusive") - }; - Nat64.toNat(await* uniform64(Nat64.fromNat(toExclusive - fromInclusive - 1))) + fromInclusive - }; - - /// Random `Int` value in the range [fromInclusive, toExclusive). - /// @deprecated M0235 - public func intRange(fromInclusive : Int, toExclusive : Int) : async* Int { - let range = Nat.fromInt(toExclusive - fromInclusive - 1); - Nat64.toNat(await* uniform64(Nat64.fromNat(range))) + fromInclusive - }; - - }; - - // Derived from https://github.com/research-ag/prng - module PRNG { - let p : Nat64 = 24; - let q : Nat64 = 11; - let r : Nat64 = 3; - - public type State = { - var a : Nat64; - var b : Nat64; - var c : Nat64; - var d : Nat64 - }; - - public func init(seed : Nat64) : State { - init3(seed, seed, seed) - }; - - public func init3(seed1 : Nat64, seed2 : Nat64, seed3 : Nat64) : State { - let state : State = { - var a = seed1; - var b = seed2; - var c = seed3; - var d = 1 - }; - for (_ in Nat.range(0, 11)) ignore next(state); - state - }; - - public func next(state : State) : Nat64 { - let tmp = state.a +% state.b +% state.d; - state.a := state.b ^ (state.b >> q); - state.b := state.c +% (state.c << r); - state.c := (state.c <<> p) +% tmp; - state.d +%= 1; - tmp - } - } - -} diff --git a/.mops/core@2.3.1/src/Region.mo b/.mops/core@2.3.1/src/Region.mo deleted file mode 100644 index a08783a..0000000 --- a/.mops/core@2.3.1/src/Region.mo +++ /dev/null @@ -1,485 +0,0 @@ -/// Byte-level access to isolated, virtual stable memory regions. -/// -/// This is a moderately lightweight abstraction over IC _stable memory_ and supports persisting -/// regions of binary data across Motoko upgrades. -/// Use of this module is fully compatible with Motoko's use of -/// _stable variables_, whose persistence mechanism also uses (real) IC stable memory internally, but does not interfere with this API. -/// It is also fully compatible with existing uses of the `ExperimentalStableMemory` library, which has a similar interface, but, -/// only supported a single memory region, without isolation between different applications. -/// -/// The `Region` type is stable and can be used in stable data structures. -/// -/// A new, empty `Region` is allocated using function `new()`. -/// -/// Regions are stateful objects and can be distinguished by the numeric identifier returned by function `id(region)`. -/// Every region owns an initially empty, but growable sequence of virtual IC stable memory pages. -/// The current size, in pages, of a region is returned by function `size(region)`. -/// The size of a region determines the range, [ 0, ..., size(region)*2^16 ), of valid byte-offsets into the region; these offsets are used as the source and destination of `load`/`store` operations on the region. -/// -/// Memory is allocated to a region, using function `grow(region, pages)`, sequentially and on demand, in units of 64KiB logical pages, starting with 0 allocated pages. -/// A call to `grow` may succeed, returning the previous size of the region, or fail, returning a sentinel value. New pages are zero initialized. -/// -/// A size of a region can only grow and never shrink. -/// In addition, the stable memory pages allocated to a region will *not* be reclaimed by garbage collection, even -/// if the region object itself becomes unreachable. -/// -/// Growth is capped by a soft limit on physical page count controlled by compile-time flag -/// `--max-stable-pages ` (the default is 65536, or 4GiB). -/// -/// Each `load` operation loads from region relative byte address `offset` in little-endian -/// format using the natural bit-width of the type in question. -/// The operation traps if attempting to read beyond the current region size. -/// -/// Each `store` operation stores to region relative byte address `offset` in little-endian format using the natural bit-width of the type in question. -/// The operation traps if attempting to write beyond the current region size. -/// -/// Text values can be handled by using `Text.decodeUtf8` and `Text.encodeUtf8`, in conjunction with `loadBlob` and `storeBlob`. -/// -/// The current region allocation and region contents are preserved across upgrades. -/// -/// NB: The IC's actual stable memory size (`ic0.stable_size`) may exceed the -/// total page size reported by summing all regions sizes. -/// This (and the cap on growth) are to accommodate Motoko's stable variables and bookkeeping for regions. -/// Applications that plan to use Motoko stable variables sparingly or not at all can -/// increase `--max-stable-pages` as desired, approaching the IC maximum (initially 8GiB, then 32Gib, currently 64Gib). -/// All applications should reserve at least one page for stable variable data, even when no stable variables are used. -/// -/// Usage: -/// ```motoko no-repl name=import -/// import Region "mo:core/Region"; -/// ``` - -import Prim "mo:⛔"; - -module { - - /// A stateful handle to an isolated region of IC stable memory. - /// `Region` is a stable type and regions can be stored in stable variables. - /// @deprecated M0235 - public type Region = Prim.Types.Region; - - /// Allocate a new, isolated Region of size 0. - /// - /// Example: - /// - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// assert Region.size(region) == 0; - /// } - /// } - /// ``` - public let new : () -> Region = Prim.regionNew; - - /// Return a Nat identifying the given region. - /// May be used for equality, comparison and hashing. - /// NB: Regions returned by `new()` are numbered from 16 - /// (regions 0..15 are currently reserved for internal use). - /// Allocate a new, isolated Region of size 0. - /// - /// Example: - /// - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// assert Region.id(region) == 16; - /// } - /// } - /// ``` - public let id : (self : Region) -> Nat = Prim.regionId; - - /// Current size of `region`, in pages. - /// Each page is 64KiB (65536 bytes). - /// Initially `0`. - /// Preserved across upgrades, together with contents of allocated - /// stable memory. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let beforeSize = Region.size(region); - /// ignore Region.grow(region, 10); - /// let afterSize = Region.size(region); - /// assert afterSize - beforeSize == 10; - /// } - /// } - /// ``` - public let size : (self : Region) -> (pages : Nat64) = Prim.regionSize; - - /// Grow current `size` of `region` by the given number of pages. - /// Each page is 64KiB (65536 bytes). - /// Returns the previous `size` when able to grow. - /// Returns `0xFFFF_FFFF_FFFF_FFFF` if remaining pages insufficient. - /// Every new page is zero-initialized, containing byte 0x00 at every offset. - /// Function `grow` is capped by a soft limit on `size` controlled by compile-time flag - /// `--max-stable-pages ` (the default is 65536, or 4GiB). - /// - /// Example: - /// ```motoko no-repl include=import - /// import Error "mo:core/Error"; - /// - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let beforeSize = Region.grow(region, 10); - /// if (beforeSize == 0xFFFF_FFFF_FFFF_FFFF) { - /// throw Error.reject("Out of memory"); - /// }; - /// let afterSize = Region.size(region); - /// assert afterSize - beforeSize == 10; - /// } - /// } - /// ``` - public let grow : (self : Region, newPages : Nat64) -> (oldPages : Nat64) = Prim.regionGrow; - - /// Within `region`, load a `Nat8` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat8 = 123; - /// Region.storeNat8(region, offset, value); - /// assert Region.loadNat8(region, offset) == 123; - /// } - /// } - /// ``` - public let loadNat8 : (self : Region, offset : Nat64) -> Nat8 = Prim.regionLoadNat8; - - /// Within `region`, store a `Nat8` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat8 = 123; - /// Region.storeNat8(region, offset, value); - /// assert Region.loadNat8(region, offset) == 123; - /// } - /// } - /// ``` - public let storeNat8 : (self : Region, offset : Nat64, value : Nat8) -> () = Prim.regionStoreNat8; - - /// Within `region`, load a `Nat16` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat16 = 123; - /// Region.storeNat16(region, offset, value); - /// assert Region.loadNat16(region, offset) == 123; - /// } - /// } - /// ``` - public let loadNat16 : (self : Region, offset : Nat64) -> Nat16 = Prim.regionLoadNat16; - - /// Within `region`, store a `Nat16` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat16 = 123; - /// Region.storeNat16(region, offset, value); - /// assert Region.loadNat16(region, offset) == 123; - /// } - /// } - /// ``` - public let storeNat16 : (self : Region, offset : Nat64, value : Nat16) -> () = Prim.regionStoreNat16; - - /// Within `region`, load a `Nat32` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat32 = 123; - /// Region.storeNat32(region, offset, value); - /// assert Region.loadNat32(region, offset) == 123; - /// } - /// } - /// ``` - public let loadNat32 : (self : Region, offset : Nat64) -> Nat32 = Prim.regionLoadNat32; - - /// Within `region`, store a `Nat32` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat32 = 123; - /// Region.storeNat32(region, offset, value); - /// assert Region.loadNat32(region, offset) == 123; - /// } - /// } - /// ``` - public func storeNat32(self : Region, offset : Nat64, value : Nat32) : () = Prim.regionStoreNat32(self, offset, value); - - /// Within `region`, load a `Nat64` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat64 = 123; - /// Region.storeNat64(region, offset, value); - /// assert Region.loadNat64(region, offset) == 123; - /// } - /// } - /// ``` - public let loadNat64 : (self : Region, offset : Nat64) -> Nat64 = Prim.regionLoadNat64; - - /// Within `region`, store a `Nat64` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat64 = 123; - /// Region.storeNat64(region, offset, value); - /// assert Region.loadNat64(region, offset) == 123; - /// } - /// } - /// ``` - public let storeNat64 : (self : Region, offset : Nat64, value : Nat64) -> () = Prim.regionStoreNat64; - - /// Within `region`, load a `Int8` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int8 = 123; - /// Region.storeInt8(region, offset, value); - /// assert Region.loadInt8(region, offset) == 123; - /// } - /// } - /// ``` - public let loadInt8 : (self : Region, offset : Nat64) -> Int8 = Prim.regionLoadInt8; - - /// Within `region`, store a `Int8` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int8 = 123; - /// Region.storeInt8(region, offset, value); - /// assert Region.loadInt8(region, offset) == 123; - /// } - /// } - /// ``` - public let storeInt8 : (self : Region, offset : Nat64, value : Int8) -> () = Prim.regionStoreInt8; - - /// Within `region`, load a `Int16` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int16 = 123; - /// Region.storeInt16(region, offset, value); - /// assert Region.loadInt16(region, offset) == 123; - /// } - /// } - /// ``` - public let loadInt16 : (self : Region, offset : Nat64) -> Int16 = Prim.regionLoadInt16; - - /// Within `region`, store a `Int16` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int16 = 123; - /// Region.storeInt16(region, offset, value); - /// assert Region.loadInt16(region, offset) == 123; - /// } - /// } - /// ``` - public let storeInt16 : (self : Region, offset : Nat64, value : Int16) -> () = Prim.regionStoreInt16; - - /// Within `region`, load a `Int32` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int32 = 123; - /// Region.storeInt32(region, offset, value); - /// assert Region.loadInt32(region, offset) == 123; - /// } - /// } - /// ``` - public let loadInt32 : (self : Region, offset : Nat64) -> Int32 = Prim.regionLoadInt32; - - /// Within `region`, store a `Int32` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int32 = 123; - /// Region.storeInt32(region, offset, value); - /// assert Region.loadInt32(region, offset) == 123; - /// } - /// } - /// ``` - public let storeInt32 : (self : Region, offset : Nat64, value : Int32) -> () = Prim.regionStoreInt32; - - /// Within `region`, load a `Int64` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int64 = 123; - /// Region.storeInt64(region, offset, value); - /// assert Region.loadInt64(region, offset) == 123; - /// } - /// } - /// ``` - public let loadInt64 : (self : Region, offset : Nat64) -> Int64 = Prim.regionLoadInt64; - - /// Within `region`, store a `Int64` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int64 = 123; - /// Region.storeInt64(region, offset, value); - /// assert Region.loadInt64(region, offset) == 123; - /// } - /// } - /// ``` - public let storeInt64 : (self : Region, offset : Nat64, value : Int64) -> () = Prim.regionStoreInt64; - - /// Within `region`, loads a `Float` value from the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value = 1.25; - /// Region.storeFloat(region, offset, value); - /// assert Region.loadFloat(region, offset) == 1.25; - /// } - /// } - /// ``` - public let loadFloat : (self : Region, offset : Nat64) -> Float = Prim.regionLoadFloat; - - /// Within `region`, store float `value` at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value = 1.25; - /// Region.storeFloat(region, offset, value); - /// assert Region.loadFloat(region, offset) == 1.25; - /// } - /// } - /// ``` - public let storeFloat : (self : Region, offset : Nat64, value : Float) -> () = Prim.regionStoreFloat; - - /// Within `region,` load `size` bytes starting from `offset` as a `Blob`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// import Blob "mo:core/Blob"; - /// - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value = Blob.fromArray([1, 2, 3]); - /// let size = value.size(); - /// Region.storeBlob(region, offset, value); - /// assert Blob.toArray(Region.loadBlob(region, offset, size)) == [1, 2, 3]; - /// } - /// } - /// ``` - public let loadBlob : (self : Region, offset : Nat64, size : Nat) -> Blob = Prim.regionLoadBlob; - - /// Within `region, write `blob.size()` bytes of `blob` beginning at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// import Blob "mo:core/Blob"; - /// - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value = Blob.fromArray([1, 2, 3]); - /// let size = value.size(); - /// Region.storeBlob(region, offset, value); - /// assert Blob.toArray(Region.loadBlob(region, offset, size)) == [1, 2, 3]; - /// } - /// } - /// ``` - public let storeBlob : (self : Region, offset : Nat64, value : Blob) -> () = Prim.regionStoreBlob; - -} diff --git a/.mops/core@2.3.1/src/Result.mo b/.mops/core@2.3.1/src/Result.mo deleted file mode 100644 index 08aa478..0000000 --- a/.mops/core@2.3.1/src/Result.mo +++ /dev/null @@ -1,355 +0,0 @@ -/// Module for error handling with the Result type. -/// -/// The Result type is used for returning and propagating errors. It has two variants: -/// `#ok(Ok)`, representing success and containing a value, and `#err(Err)`, representing -/// error and containing an error value. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Result "mo:core/Result"; -/// ``` - -import Order "Order"; -import Types "Types"; - -module { - - /// The Result type used for returning and propagating errors. - /// - /// The simplest way of working with Results is to pattern match on them. - /// For example: - /// ```motoko include=import - /// import Text "mo:core/Text"; - /// - /// type Email = Text; - /// type ErrorMessage = Text; - /// - /// func validateEmail(email : Text) : Result.Result { - /// let parts = Text.split(email, #char '@'); - /// let beforeAt = parts.next(); - /// let afterAt = parts.next(); - /// switch (beforeAt, afterAt) { - /// case (?local, ?domain) { - /// if (local == "") return #err("Username cannot be empty"); - /// if (not Text.contains(domain, #char '.')) return #err("Invalid domain format"); - /// #ok(email) - /// }; - /// case _ #err("Email must contain exactly one @ symbol") - /// } - /// }; - /// - /// assert validateEmail("user@example.com") == #ok("user@example.com"); - /// assert validateEmail("invalid.email") == #err("Email must contain exactly one @ symbol"); - /// assert validateEmail("@domain.com") == #err("Username cannot be empty"); - /// assert validateEmail("user@invalid") == #err("Invalid domain format"); - /// ``` - /// @deprecated M0235 - public type Result = Types.Result; - - /// Compares two Results for equality. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let result1 = #ok 10; - /// let result2 = #ok 10; - /// let result3 = #err "error"; - /// - /// assert Result.equal(result1, result2, Nat.equal, Text.equal); - /// assert not Result.equal(result1, result3, Nat.equal, Text.equal); - /// ``` - public func equal( - self : Result, - other : Result, - equalOk : (implicit : (equal : Ok, Ok) -> Bool), - equalErr : (implicit : (equal : (Err, Err) -> Bool)) - ) : Bool { - switch (self, other) { - case (#ok(ok1), #ok(ok2)) { - equalOk(ok1, ok2) - }; - case (#err(err1), #err(err2)) { - equalErr(err1, err2) - }; - case _ { false } - } - }; - - /// Compares two Result values. `#ok` is larger than `#err`. This ordering is - /// arbitrary, but it lets you for example use Results as keys in ordered maps. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let result1 = #ok 5; - /// let result2 = #ok 10; - /// let result3 = #err "error"; - /// - /// assert Result.compare(result1, result2, Nat.compare, Text.compare) == #less; - /// assert Result.compare(result2, result1, Nat.compare, Text.compare) == #greater; - /// assert Result.compare(result1, result3, Nat.compare, Text.compare) == #greater; - /// ``` - public func compare( - self : Result, - other : Result, - compareOk : (implicit : (compare : (Ok, Ok) -> Order.Order)), - compareErr : (implicit : (compare : (Err, Err) -> Order.Order)) - ) : Order.Order { - switch (self, other) { - case (#ok(ok1), #ok(ok2)) { - compareOk(ok1, ok2) - }; - case (#err(err1), #err(err2)) { - compareErr(err1, err2) - }; - case (#ok(_), _) { #greater }; - case (#err(_), _) { #less } - } - }; - - /// Allows sequencing of Result values and functions that return - /// Results themselves. - /// ```motoko include=import - /// type Result = Result.Result; - /// func largerThan10(x : Nat) : Result = - /// if (x > 10) { #ok(x) } else { #err("Not larger than 10.") }; - /// - /// func smallerThan20(x : Nat) : Result = - /// if (x < 20) { #ok(x) } else { #err("Not smaller than 20.") }; - /// - /// func between10And20(x : Nat) : Result = - /// Result.chain(largerThan10(x), smallerThan20); - /// - /// assert between10And20(15) == #ok(15); - /// assert between10And20(9) == #err("Not larger than 10."); - /// assert between10And20(21) == #err("Not smaller than 20."); - /// ``` - public func chain( - self : Result, - f : Ok1 -> Result - ) : Result { - switch self { - case (#err(e)) { #err(e) }; - case (#ok(r)) { f(r) } - } - }; - - /// Flattens a nested Result. - /// - /// ```motoko include=import - /// assert Result.flatten(#ok(#ok(10))) == #ok(10); - /// assert Result.flatten(#err("Wrong")) == #err("Wrong"); - /// assert Result.flatten(#ok(#err("Wrong"))) == #err("Wrong"); - /// ``` - public func flatten( - self : Result, Err> - ) : Result { - switch self { - case (#ok(ok)) { ok }; - case (#err(err)) { #err(err) } - } - }; - - /// Maps the `Ok` type/value, leaving any `Err` type/value unchanged. - /// - /// Example: - /// ```motoko include=import - /// let result1 = #ok(42); - /// let result2 = #err("error"); - /// - /// let doubled1 = Result.mapOk(result1, func x = x * 2); - /// assert doubled1 == #ok(84); - /// - /// let doubled2 = Result.mapOk(result2, func x = x * 2); - /// assert doubled2 == #err("error"); - /// ``` - public func mapOk( - self : Result, - f : Ok1 -> Ok2 - ) : Result { - switch self { - case (#err(e)) { #err(e) }; - case (#ok(r)) { #ok(f(r)) } - } - }; - - /// Maps the `Err` type/value, leaving any `Ok` type/value unchanged. - /// - /// Example: - /// ```motoko include=import - /// let result1 = #ok(42); - /// let result2 = #err("error"); - /// - /// let mapped1 = Result.mapErr(result1, func x = x # "!"); - /// assert mapped1 == #ok(42); - /// - /// let mapped2 = Result.mapErr(result2, func x = x # "!"); - /// assert mapped2 == #err("error!"); - /// ``` - public func mapErr( - self : Result, - f : Err1 -> Err2 - ) : Result { - switch self { - case (#err(e)) { #err(f(e)) }; - case (#ok(r)) { #ok(r) } - } - }; - - /// Create a result from an option, including an error value to handle the `null` case. - /// ```motoko include=import - /// assert Result.fromOption(?42, "err") == #ok(42); - /// assert Result.fromOption(null, "err") == #err("err"); - /// ``` - public func fromOption(x : ?Ok, err : Err) : Result { - switch x { - case (?x) { #ok(x) }; - case null { #err(err) } - } - }; - - /// Create an option from a result, turning all #err into `null`. - /// ```motoko include=import - /// assert Result.toOption(#ok(42)) == ?42; - /// assert Result.toOption(#err("err")) == null; - /// ``` - public func toOption(self : Result) : ?Ok { - switch self { - case (#ok(x)) { ?x }; - case (#err(_)) { null } - } - }; - - /// Applies a function to a successful value and discards the result. Use - /// `forOk` if you're only interested in the side effect `f` produces. - /// - /// ```motoko include=import - /// var counter : Nat = 0; - /// Result.forOk(#ok(5), func (x : Nat) { counter += x }); - /// assert counter == 5; - /// Result.forOk(#err("Error"), func (x : Nat) { counter += x }); - /// assert counter == 5; - /// ``` - public func forOk(self : Result, f : Ok -> ()) { - switch self { - case (#ok(ok)) { f(ok) }; - case _ {} - } - }; - - /// Applies a function to an error value and discards the result. Use - /// `forErr` if you're only interested in the side effect `f` produces. - /// - /// ```motoko include=import - /// var counter : Nat = 0; - /// Result.forErr(#err("Error"), func (x : Text) { counter += 1 }); - /// assert counter == 1; - /// Result.forErr(#ok(5), func (x : Text) { counter += 1 }); - /// assert counter == 1; - /// ``` - public func forErr(self : Result, f : Err -> ()) { - switch self { - case (#err(err)) { f(err) }; - case _ {} - } - }; - - /// Whether this Result is an `#ok`. - /// - /// Example: - /// ```motoko include=import - /// assert Result.isOk(#ok(42)); - /// assert not Result.isOk(#err("error")); - /// ``` - public func isOk(self : Result) : Bool { - switch self { - case (#ok(_)) { true }; - case (#err(_)) { false } - } - }; - - /// Whether this Result is an `#err`. - /// - /// Example: - /// ```motoko include=import - /// assert Result.isErr(#err("error")); - /// assert not Result.isErr(#ok(42)); - /// ``` - public func isErr(self : Result) : Bool { - switch self { - case (#ok(_)) { false }; - case (#err(_)) { true } - } - }; - - /// Asserts that its argument is an `#ok` result, traps otherwise. - /// - /// Example: - /// ```motoko include=import - /// Result.assertOk(#ok(42)); // succeeds - /// // Result.assertOk(#err("error")); // would trap - /// ``` - public func assertOk(self : Result) { - switch self { - case (#err(_)) { assert false }; - case (#ok(_)) {} - } - }; - - /// Asserts that its argument is an `#err` result, traps otherwise. - /// - /// Example: - /// ```motoko include=import - /// Result.assertErr(#err("error")); // succeeds - /// // Result.assertErr(#ok(42)); // would trap - /// ``` - public func assertErr(self : Result) { - switch self { - case (#err(_)) {}; - case (#ok(_)) assert false - } - }; - - /// Converts an upper cased `#Ok`, `#Err` result type into a lowercased `#ok`, `#err` result type. - /// On the IC, a common convention is to use `#Ok` and `#Err` as the variants of a result type, - /// but in Motoko, we use `#ok` and `#err` instead. - /// - /// Example: - /// ```motoko include=import - /// let upper = #Ok(42); - /// let lower = Result.fromUpper(upper); - /// assert lower == #ok(42); - /// ``` - public func fromUpper( - result : { #Ok : Ok; #Err : Err } - ) : Result { - switch result { - case (#Ok(ok)) { #ok(ok) }; - case (#Err(err)) { #err(err) } - } - }; - - /// Converts a lower cased `#ok`, `#err` result type into an upper cased `#Ok`, `#Err` result type. - /// On the IC, a common convention is to use `#Ok` and `#Err` as the variants of a result type, - /// but in Motoko, we use `#ok` and `#err` instead. - /// - /// Example: - /// ```motoko include=import - /// let lower = #ok(42); - /// let upper = Result.toUpper(lower); - /// assert upper == #Ok(42); - /// ``` - public func toUpper( - self : Result - ) : { #Ok : Ok; #Err : Err } { - switch self { - case (#ok(ok)) { #Ok(ok) }; - case (#err(err)) { #Err(err) } - } - }; - -} diff --git a/.mops/core@2.3.1/src/Runtime.mo b/.mops/core@2.3.1/src/Runtime.mo deleted file mode 100644 index 4a797a1..0000000 --- a/.mops/core@2.3.1/src/Runtime.mo +++ /dev/null @@ -1,70 +0,0 @@ -/// Runtime utilities. -/// These functions were originally part of the `Debug` module. -/// -/// ```motoko name=import -/// import Runtime "mo:core/Runtime"; -/// ``` -import Prim "mo:⛔"; - -module { - - /// `trap(t)` traps execution with a user-provided diagnostic message. - /// - /// The caller of a future whose execution called `trap(t)` will - /// observe the trap as an `Error` value, thrown at `await`, with code - /// `#canister_error` and message `m`. Here `m` is a more descriptive `Text` - /// message derived from the provided `t`. See example for more details. - /// - /// NOTE: Other execution environments that cannot handle traps may only - /// propagate the trap and terminate execution, with or without some - /// descriptive message. - /// - /// ```motoko include=import no-validate - /// Runtime.trap("An error occurred!"); - /// ``` - public func trap(errorMessage : Text) : None { - Prim.trap errorMessage - }; - - /// `unreachable()` traps execution when code that should be unreachable is reached. - /// - /// This function is useful for marking code paths that should never be executed, - /// such as after exhaustive pattern matches or unreachable control flow branches. - /// If execution reaches this function, it indicates a programming error. - /// - /// ```motoko include=import no-validate - /// let number = switch (?5) { - /// case (?n) n; - /// case null Runtime.unreachable(); - /// }; - /// assert number == 5; - /// ``` - public func unreachable() : None { - trap("Runtime.unreachable()") - }; - - /// Returns the names of all canister environment variables. - /// - /// Example: - /// ```motoko include=import no-validate - /// let names = Runtime.envVarNames(); - /// ``` - public func envVarNames() : [Text] { - return Prim.envVarNames() - }; - - /// Returns an optional value of the canister environment variable with the given name. - /// - /// Example: - /// ```motoko include=import no-validate - /// let value = Runtime.envVar("MY_ENV_VAR"); - /// let result = switch (value) { - /// case (?v) v; - /// case null Runtime.trap("Unknown environment variable"); - /// }; - /// ``` - public func envVar(name : Text) : ?Text { - return Prim.envVar(name) - } - -} diff --git a/.mops/core@2.3.1/src/Set.mo b/.mops/core@2.3.1/src/Set.mo deleted file mode 100644 index 20ad4f5..0000000 --- a/.mops/core@2.3.1/src/Set.mo +++ /dev/null @@ -1,2756 +0,0 @@ -/// Imperative (mutable) sets based on order/comparison of elements. -/// A set is a collection of elements without duplicates. -/// The set data structure type is stable and can be used for orthogonal persistence. -/// -/// Example: -/// ```motoko -/// import Set "mo:core/Set"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// let set = Set.fromIter([3, 1, 2, 3].vals(), Nat.compare); -/// assert Set.size(set) == 3; -/// assert not Set.contains(set, Nat.compare, 4); -/// let diff = Set.difference(set, set, Nat.compare); -/// assert Set.isEmpty(diff); -/// } -/// ``` -/// -/// These sets are implemented as B-trees with order 32, a balanced search tree of ordered elements. -/// -/// Performance: -/// * Runtime: `O(log(n))` worst case cost per insertion, removal, and retrieval operation. -/// * Space: `O(n)` for storing the entire tree, -/// where `n` denotes the number of elements stored in the set. - -// Data structure implementation is courtesy of Byron Becker. -// Source: https://github.com/canscale/StableHeapBTreeMap -// Copyright (c) 2022 Byron Becker. -// Distributed under Apache 2.0 license. -// With adjustments by the Motoko team. - -import PureSet "pure/Set"; -import Types "Types"; -import Order "Order"; -import Array "Array"; -import VarArray "VarArray"; -import Runtime "Runtime"; -import Stack "Stack"; -import Option "Option"; -import Iter "Iter"; -import BTreeHelper "internal/BTreeHelper"; - -module { - let btreeOrder = 32; // Should be >= 4 and <= 512. - - public type Set = Types.Set.Set; - type Node = Types.Set.Node; - type Data = Types.Set.Data; - type Internal = Types.Set.Internal; - type Leaf = Types.Set.Leaf; - - /// Convert the mutable set to an immutable, purely functional set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import PureSet "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 1].values(), Nat.compare); - /// let pureSet = Set.toPure(set, Nat.compare); - /// assert Iter.toArray(PureSet.values(pureSet)) == Iter.toArray(Set.values(set)); - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - /// @deprecated M0235 - public func toPure(self : Set, compare : (implicit : (T, T) -> Order.Order)) : PureSet.Set { - PureSet.fromIter(values(self), compare) - }; - - /// Convert an immutable, purely functional set to a mutable set. - /// - /// Example: - /// ```motoko - /// import PureSet "mo:core/pure/Set"; - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let pureSet = PureSet.fromIter([3, 1, 2].values(), Nat.compare); - /// let set = Set.fromPure(pureSet, Nat.compare); - /// assert Iter.toArray(Set.values(set)) == Iter.toArray(PureSet.values(pureSet)); - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func fromPure(set : PureSet.Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - fromIter(PureSet.values(set), compare) - }; - - public func fromArray(array : [T], compare : (implicit : (T, T) -> Order.Order)) : Set { - fromIter(array.values(), compare) - }; - - /// Create a copy of the mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let originalSet = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let clonedSet = Set.clone(originalSet); - /// Set.add(originalSet, Nat.compare, 4); - /// assert Set.size(clonedSet) == 3; - /// assert Set.size(originalSet) == 4; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the set. - public func clone(self : Set) : Set { - { - var root = cloneNode(self.root); - var size = self.size - } - }; - - /// Create a new empty mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Set.size(set) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Set { - { - var root = #leaf({ - data = { - elements = VarArray.repeat(null, btreeOrder - 1); - var count = 0 - } - }); - var size = 0 - } - }; - - /// Create a new mutable set with a single element. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// - /// persistent actor { - /// let cities = Set.singleton("Zurich"); - /// assert Set.size(cities) == 1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func singleton(element : T) : Set { - let elements = VarArray.repeat(null, btreeOrder - 1); - elements[0] := ?element; - { - var root = - #leaf({ data = { elements; var count = 1 } }); - var size = 1 - } - }; - - /// Remove all the elements from the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let cities = Set.empty(); - /// Set.add(cities, Text.compare, "Zurich"); - /// Set.add(cities, Text.compare, "San Francisco"); - /// Set.add(cities, Text.compare, "London"); - /// assert Set.size(cities) == 3; - /// - /// Set.clear(cities); - /// assert Set.size(cities) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func clear(self : Set) { - let emptySet = empty(); - self.root := emptySet.root; - self.size := 0 - }; - - /// Determines whether a set is empty. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 3); - /// - /// assert not Set.isEmpty(set); - /// Set.clear(set); - /// assert Set.isEmpty(set); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func isEmpty(self : Set) : Bool { - self.size == 0 - }; - - /// Return the number of elements in a set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 3); - /// - /// assert Set.size(set) == 3; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func size(self : Set) : Nat { - self.size - }; - - /// Test whether two imperative sets are equal. - /// Both sets have to be constructed by the same comparison function. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([2, 1].values(), Nat.compare); - /// let set3 = Set.fromIter([2, 1, 0].values(), Nat.compare); - /// assert Set.equal(set1, set2, Nat.compare); - /// assert not Set.equal(set1, set3, Nat.compare); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func equal(self : Set, other : Set, compare : (implicit : (T, T) -> Types.Order)) : Bool { - if (self.size != other.size) return false; - // TODO: optimize - let iterator1 = values(self); - let iterator2 = values(other); - loop { - let next1 = iterator1.next(); - let next2 = iterator2.next(); - switch (next1, next2) { - case (null, null) { - return true - }; - case (?element1, ?element2) { - if (not (compare(element1, element2) == #equal)) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Tests whether the set contains the provided element. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 3); - /// - /// assert Set.contains(set, Nat.compare, 1); - /// assert not Set.contains(set, Nat.compare, 4); - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func contains(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Bool { - switch (self.root) { - case (#internal(internalNode)) { - containsInInternal(internalNode, compare, element) - }; - case (#leaf(leafNode)) { containsInLeaf(leafNode, compare, element) } - } - }; - - /// Add a new element to a set. - /// No effect if the element already exists in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// assert Iter.toArray(Set.values(set)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func add(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) { - ignore insert(self, compare, element) - }; - - /// Insert a new element in the set. - /// Returns true if the element is new, false if the element was already contained in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Set.insert(set, Nat.compare, 2); - /// assert Set.insert(set, Nat.compare, 1); - /// assert not Set.insert(set, Nat.compare, 2); - /// assert Iter.toArray(Set.values(set)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func insert(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Bool { - let insertResult = switch (self.root) { - case (#leaf(leafNode)) { - leafInsertHelper(leafNode, btreeOrder, compare, element) - }; - case (#internal(internalNode)) { - internalInsertHelper(internalNode, btreeOrder, compare, element) - } - }; - - switch (insertResult) { - case (#inserted) { - // if inserted an element that was not previously there, increment the tree size counter - self.size += 1; - true - }; - case (#existent) { - // keep size - false - }; - case (#promote({ element = promotedElement; leftChild; rightChild })) { - let elements = VarArray.repeat(null, btreeOrder - 1); - elements[0] := ?promotedElement; - let children = VarArray.repeat>(null, btreeOrder); - children[0] := ?leftChild; - children[1] := ?rightChild; - self.root := #internal({ - data = { elements; var count = 1 }; - children - }); - // promotion always comes from inserting a new element, so increment the tree size counter - self.size += 1; - true - } - } - }; - - /// Deletes an element from a set. - /// No effect if the element is not contained in the set. - /// - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// - /// Set.remove(set, Nat.compare, 2); - /// assert not Set.contains(set, Nat.compare, 2); - /// - /// Set.remove(set, Nat.compare, 4); - /// assert not Set.contains(set, Nat.compare, 4); - /// - /// assert Iter.toArray(Set.values(set)) == [1, 3]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - public func remove(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : () { - ignore delete(self, compare, element) - }; - - /// Deletes an element from a set. - /// Returns true if the element was contained in the set, false if not. - /// - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// - /// assert Set.delete(set, Nat.compare, 2); - /// assert not Set.contains(set, Nat.compare, 2); - /// - /// assert not Set.delete(set, Nat.compare, 4); - /// assert not Set.contains(set, Nat.compare, 4); - /// assert Iter.toArray(Set.values(set)) == [1, 3]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// @deprecated M0235 - public func delete(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Bool { - let deleted = switch (self.root) { - case (#leaf(leafNode)) { - // TODO: think about how this can be optimized so don't have to do two steps (search and then insert)? - switch (NodeUtil.getElementIndex(leafNode.data, compare, element)) { - case (#elementFound(deleteIndex)) { - leafNode.data.count -= 1; - ignore BTreeHelper.deleteAndShift(leafNode.data.elements, deleteIndex); - self.size -= 1; - true - }; - case _ { false } - } - }; - case (#internal(internalNode)) { - let deletedElement = switch (internalDeleteHelper(internalNode, btreeOrder, compare, element, false)) { - case (#deleted) { true }; - case (#inexistent) { false }; - case (#mergeChild({ internalChild })) { - if (internalChild.data.count > 0) { - self.root := #internal(internalChild) - } - // This case will be hit if the BTree has order == 4 - // In this case, the internalChild has no element (last element was merged with new child), so need to promote that merged child (its only child) - else { - self.root := switch (internalChild.children[0]) { - case (?node) { node }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.delete(), element deletion failed, due to a null replacement node error") - } - } - }; - true - } - }; - if (deletedElement) { - // if deleted an element from the BTree, decrement the size - self.size -= 1 - }; - deletedElement - } - }; - deleted - }; - - /// Retrieves the maximum element from the set. - /// If the set is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Set.max(set) == null; - /// Set.add(set, Nat.compare, 3); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// assert Set.max(set) == ?3; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set. - public func max(self : Set) : ?T { - reverseValues(self).next() - }; - - /// Retrieves the minimum element from the set. - /// If the set is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Set.min(set) == null; - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 3); - /// assert Set.min(set) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set. - public func min(self : Set) : ?T { - values(self).next() - }; - - public func toArray(self : Set) : [T] { - Iter.toArray(values(self)) - }; - - /// Returns an iterator over the elements in the set, - /// traversing the elements in the ascending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 3, 1].values(), Nat.compare); - /// - /// var tmp = ""; - /// for (number in Set.values(set)) { - /// tmp #= " " # Nat.toText(number); - /// }; - /// assert tmp == " 0 1 2 3"; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func values(self : Set) : Types.Iter { - switch (self.root) { - case (#leaf(leafNode)) { return leafElements(leafNode) }; - case (#internal(internalNode)) { internalElements(internalNode) } - } - }; - - /// Returns an iterator over the elements in the set, - /// starting from a given element in ascending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1].values(), Nat.compare); - /// assert Iter.toArray(Set.valuesFrom(set, Nat.compare, 1)) == [1, 3]; - /// assert Iter.toArray(Set.valuesFrom(set, Nat.compare, 2)) == [3]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func valuesFrom( - self : Set, - compare : (implicit : (T, T) -> Order.Order), - element : T - ) : Types.Iter { - switch (self.root) { - case (#leaf(leafNode)) leafElementsFrom(leafNode, compare, element); - case (#internal(internalNode)) internalElementsFrom(internalNode, compare, element) - } - }; - - /// Returns an iterator over the elements in the set, - /// traversing the elements in the descending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 3, 1].values(), Nat.compare); - /// - /// var tmp = ""; - /// for (number in Set.reverseValues(set)) { - /// tmp #= " " # Nat.toText(number); - /// }; - /// assert tmp == " 3 2 1 0"; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseValues(self : Set) : Types.Iter { - switch (self.root) { - case (#leaf(leafNode)) { return reverseLeafElements(leafNode) }; - case (#internal(internalNode)) { reverseInternalElements(internalNode) } - } - }; - - /// Returns an iterator over the elements in the set, - /// starting from a given element in descending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 1, 3].values(), Nat.compare); - /// assert Iter.toArray(Set.reverseValuesFrom(set, Nat.compare, 0)) == [0]; - /// assert Iter.toArray(Set.reverseValuesFrom(set, Nat.compare, 2)) == [1, 0]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseValuesFrom( - self : Set, - compare : (implicit : (T, T) -> Order.Order), - element : T - ) : Types.Iter { - switch (self.root) { - case (#leaf(leafNode)) reverseLeafElementsFrom(leafNode, compare, element); - case (#internal(internalNode)) reverseInternalElementsFrom(internalNode, compare, element) - } - }; - - /// Create a mutable set with the elements obtained from an iterator. - /// Potential duplicate elements in the iterator are ignored, i.e. - /// multiple occurrence of an equal element only occur once in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([3, 1, 2, 1].values(), Nat.compare); - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func fromIter(iter : Types.Iter, compare : (implicit : (T, T) -> Order.Order)) : Set { - let set = empty(); - for (element in iter) { - add(set, compare, element) - }; - set - }; - - /// Convert an iterator of elements to a mutable set. - /// Potential duplicate elements in the iterator are ignored, i.e. - /// multiple occurrence of an equal element only occur once in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = [3, 1, 2, 1].values(); - /// - /// let set = iter.toSet(Nat.compare); - /// - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func toSet(self : Types.Iter, compare : (implicit : (T, T) -> Order.Order)) : Set { - fromIter(self, compare) - }; - - /// Test whether `set1` is a sub-set of `set2`, i.e. each element in `set1` is - /// also contained in `set2`. Returns `true` if both sets are equal. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([2, 1, 0].values(), Nat.compare); - /// let set3 = Set.fromIter([3, 4].values(), Nat.compare); - /// assert Set.isSubset(set1, set2, Nat.compare); - /// assert not Set.isSubset(set1, set3, Nat.compare); - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func isSubset(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Bool { - if (self.size > other.size) { return false }; - // TODO: optimize - for (element in values(self)) { - if (not contains(other, compare, element)) { - return false - } - }; - true - }; - - /// Returns a new set that is the union of `set1` and `set2`, - /// i.e. a new set that all the elements that exist in at least on of the two sets. - /// Potential duplicates are ignored, i.e. if the same element occurs in both `set1` - /// and `set2`, it only occurs once in the returned set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let union = Set.union(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(union)) == [1, 2, 3, 4, 5]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func union(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = clone(self); - for (element in values(other)) { - if (not contains(result, compare, element)) { - add(result, compare, element) - } - }; - result - }; - - /// Returns a new set that is the intersection of `set1` and `set2`, - /// i.e. a new set that contains all the elements that exist in both sets. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let intersection = Set.intersection(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(intersection)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func intersection(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = empty(); - for (element in values(self)) { - if (contains(other, compare, element)) { - add(result, compare, element) - } - }; - result - }; - - /// Returns a new set that is the difference between `set1` and `set2` (`set1` minus `set2`), - /// i.e. a new set that contains all the elements of `set1` that do not exist in `set2`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let difference = Set.difference(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(difference)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func difference(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = empty(); - for (element in values(self)) { - if (not contains(other, compare, element)) { - add(result, compare, element) - } - }; - result - }; - - /// Adds all elements from `iter` to the specified `set`. - /// This is equivalent to `Set.union()` but modifies the set in place. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// Set.addAll(set, Nat.compare, [3, 4, 5].values()); - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3, 4, 5]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements in `set` and `iter`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func addAll(self : Set, compare : (implicit : (T, T) -> Order.Order), iter : Types.Iter) { - for (element in iter) { - add(self, compare, element) - } - }; - - /// Deletes all values in `iter` from the specified `set`. - /// Returns `true` if any value was present in the set, otherwise false. - /// The return value indicates whether the size of the set has changed. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 1, 2].values(), Nat.compare); - /// assert Set.deleteAll(set, Nat.compare, [0, 2].values()); - /// assert Iter.toArray(Set.values(set)) == [1]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements in `set` and `iter`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func deleteAll(self : Set, compare : (implicit : (T, T) -> Order.Order), iter : Types.Iter) : Bool { - var deleted = false; - for (element in iter) { - deleted := delete(self, compare, element) or deleted // order matters! - }; - deleted - }; - - /// Inserts all values in `iter` into `set`. - /// Returns true if any value was not contained in the original set, otherwise false. - /// The return value indicates whether the size of the set has changed. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 1, 2].values(), Nat.compare); - /// assert Set.insertAll(set, Nat.compare, [0, 2, 3].values()); - /// assert Iter.toArray(Set.values(set)) == [0, 1, 2, 3]; - /// assert not Set.insertAll(set, Nat.compare, [0, 1, 2].values()); // no change - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements in `set` and `iter`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func insertAll(self : Set, compare : (implicit : (T, T) -> Order.Order), iter : Types.Iter) : Bool { - var inserted = false; - for (element in iter) { - inserted := insert(self, compare, element) or inserted // order matters! - }; - inserted - }; - - /// Removes all values in `set` that do not satisfy the given predicate. - /// Returns `true` if and only if the size of the set has changed. - /// Modifies the set in place. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([3, 1, 2].values(), Nat.compare); - /// - /// let sizeChanged = Set.retainAll(set, Nat.compare, func n { n % 2 == 0 }); - /// assert Iter.toArray(Set.values(set)) == [2]; - /// assert sizeChanged; - /// } - /// ``` - public func retainAll(self : Set, compare : (implicit : (T, T) -> Order.Order), predicate : T -> Bool) : Bool { - let array = Array.fromIter(values(self)); - deleteAll( - self, - compare, - Iter.filter(array.vals(), func(element : T) : Bool = not predicate(element)) - ) - }; - - /// Apply an operation on each element contained in the set. - /// The operation is applied in ascending order of the elements. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// var tmp = ""; - /// Set.forEach(numbers, func (element) { - /// tmp #= " " # Nat.toText(element) - /// }); - /// assert tmp == " 0 1 2 3"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func forEach(self : Set, operation : T -> ()) { - for (element in values(self)) { - operation(element) - } - }; - - /// Filter elements in a new set. - /// Create a copy of the mutable set that only contains the elements - /// that fulfil the criterion function. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let evenNumbers = Set.filter(numbers, Nat.compare, func (number) { - /// number % 2 == 0 - /// }); - /// assert Iter.toArray(Set.values(evenNumbers)) == [0, 2]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func filter(self : Set, compare : (implicit : (T, T) -> Order.Order), criterion : T -> Bool) : Set { - let result = empty(); - for (element in values(self)) { - if (criterion(element)) { - add(result, compare, element) - } - }; - result - }; - - /// Project all elements of the set in a new set. - /// Apply a mapping function to each element in the set and - /// collect the mapped elements in a new mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([3, 1, 2].values(), Nat.compare); - /// - /// let textNumbers = - /// Set.map(numbers, Text.compare, Nat.toText); - /// assert Iter.toArray(Set.values(textNumbers)) == ["1", "2", "3"]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func map(self : Set, compare : (implicit : (T2, T2) -> Order.Order), project : T1 -> T2) : Set { - let result = empty(); - for (element1 in values(self)) { - let element2 = project(element1); - add(result, compare, element2) - }; - result - }; - - /// Filter all elements in the set by also applying a projection to the elements. - /// Apply a mapping function `project` to all elements in the set and collect all - /// elements, for which the function returns a non-null new element. Collect all - /// non-discarded new elements in a new mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([3, 0, 2, 1].values(), Nat.compare); - /// - /// let evenTextNumbers = Set.filterMap(numbers, Text.compare, func (number) { - /// if (number % 2 == 0) { - /// ?Nat.toText(number) - /// } else { - /// null // discard odd numbers - /// } - /// }); - /// assert Iter.toArray(Set.values(evenTextNumbers)) == ["0", "2"]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func filterMap(self : Set, compare : (implicit : (T2, T2) -> Order.Order), project : T1 -> ?T2) : Set { - let result = empty(); - for (element1 in values(self)) { - switch (project(element1)) { - case null {}; - case (?element2) add(result, compare, element2) - } - }; - result - }; - - /// Iterate all elements in ascending order, - /// and accumulate the elements by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1].values(), Nat.compare); - /// - /// let text = Set.foldLeft( - /// set, - /// "", - /// func (accumulator, element) { - /// accumulator # " " # Nat.toText(element) - /// } - /// ); - /// assert text == " 0 1 2 3"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func foldLeft( - self : Set, - base : A, - combine : (A, T) -> A - ) : A { - var accumulator = base; - for (element in values(self)) { - accumulator := combine(accumulator, element) - }; - accumulator - }; - - /// Iterate all elements in descending order, - /// and accumulate the elements by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1].values(), Nat.compare); - /// - /// let text = Set.foldRight( - /// set, - /// "", - /// func (element, accumulator) { - /// accumulator # " " # Nat.toText(element) - /// } - /// ); - /// assert text == " 3 2 1 0"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func foldRight( - self : Set, - base : A, - combine : (T, A) -> A - ) : A { - var accumulator = base; - for (element in reverseValues(self)) { - accumulator := combine(element, accumulator) - }; - accumulator - }; - - /// Construct the union of a series of sets, i.e. all elements of - /// each set are included in the result set. - /// Any duplicates are ignored, i.e. if an element occurs - /// in several of the iterated sets, it only occurs once in the result set. - /// - /// Assumes all sets are ordered by `compare`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let set3 = Set.fromIter([5, 6, 7].values(), Nat.compare); - /// let combined = Set.join([set1, set2, set3].values(), Nat.compare); - /// assert Iter.toArray(Set.values(combined)) == [1, 2, 3, 4, 5, 6, 7]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the iterated sets, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func join(setIterator : Types.Iter>, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = empty(); - for (set in setIterator) { - for (element in values(set)) { - add(result, compare, element) - } - }; - result - }; - - /// Construct the union of a set of element sets, i.e. all elements of - /// each element set are included in the result set. - /// Any duplicates are ignored, i.e. if the same element occurs in multiple element sets, - /// it only occurs once in the result set. - /// - /// Assumes all sets are ordered by `compare`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Order "mo:core/Order"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// func setCompare(first: Set.Set, second: Set.Set) : Order.Order { - /// Set.compare(first, second, Nat.compare) - /// }; - /// - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let set3 = Set.fromIter([5, 6, 7].values(), Nat.compare); - /// let setOfSets = Set.fromIter([set1, set2, set3].values(), setCompare); - /// let flatSet = Set.flatten(setOfSets, Nat.compare); - /// assert Iter.toArray(Set.values(flatSet)) == [1, 2, 3, 4, 5, 6, 7]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in all the sub-sets, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func flatten(self : Set>, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = empty(); - for (subSet in values(self)) { - for (element in values(subSet)) { - add(result, compare, element) - } - }; - result - }; - - /// Check whether all elements in the set satisfy a predicate, i.e. - /// the `predicate` function returns `true` for all elements in the set. - /// Returns `true` for an empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let belowTen = Set.all(set, func (number) { - /// number < 10 - /// }); - /// assert belowTen; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func all(self : Set, predicate : T -> Bool) : Bool { - // TODO optimize, avoiding iterator - for (element in values(self)) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Check whether at least one element in the set satisfies a predicate, i.e. - /// the `predicate` function returns `true` for at least one element in the set. - /// Returns `false` for an empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let aboveTen = Set.any(set, func (number) { - /// number > 10 - /// }); - /// assert not aboveTen; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func any(self : Set, predicate : T -> Bool) : Bool { - // TODO optimize, avoiding iterator - for (element in values(self)) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Internal sanity check function. - /// Can be used to check that elements have been inserted with a consistent comparison function. - /// Traps if the internal set structure is invalid. - /// @deprecated M0235 - public func assertValid(self : Set, compare : (implicit : (T, T) -> Order.Order)) { - func checkIteration(iterator : Types.Iter, order : Order.Order) { - switch (iterator.next()) { - case null {}; - case (?first) { - var previous = first; - loop { - switch (iterator.next()) { - case null return; - case (?next) { - if (compare(previous, next) != order) { - Runtime.trap("Invalid order") - }; - previous := next - } - } - } - } - } - }; - checkIteration(values(self), #less); - checkIteration(reverseValues(self), #greater) - }; - - /// Generate a textual representation of all the elements in the set. - /// Primarily to be used for testing and debugging. - /// The elements are formatted according to `elementFormat`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// assert Set.toText(set, Nat.toText) == "Set{0, 1, 2, 3}" - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that `elementFormat` has runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func toText(self : Set, toText : (implicit : T -> Text)) : Text { - var text = "Set{"; - var sep = ""; - for (element in values(self)) { - text #= sep # toText(element); - sep := ", " - }; - text # "}" - }; - - /// Compare two sets by comparing the elements. - /// Both sets must have been created by the same comparison function. - /// The two sets are iterated by the ascending order of their creation and - /// order is determined by the following rules: - /// Less: - /// `set1` is less than `set2` if: - /// * the pairwise iteration hits an element pair `element1` and `element2` where - /// `element1` is less than `element2` and all preceding elements are equal, or, - /// * `set1` is a strict prefix of `set2`, i.e. `set2` has more elements than `set1` - /// and all elements of `set1` occur at the beginning of iteration `set2`. - /// Equal: - /// `set1` and `set2` have same series of equal elements by pairwise iteration. - /// Greater: - /// `set1` is neither less nor equal `set2`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 1].values(), Nat.compare); - /// let set2 = Set.fromIter([0, 2].values(), Nat.compare); - /// - /// assert Set.compare(set1, set2, Nat.compare) == #less; - /// assert Set.compare(set1, set1, Nat.compare) == #equal; - /// assert Set.compare(set2, set1, Nat.compare) == #greater; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that `compare` has runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func compare(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let iterator1 = values(self); - let iterator2 = values(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?element1, ?element2) { - let comparison = compare(element1, element2); - if (comparison != #equal) { - return comparison - } - } - } - } - }; - - func leafElements({ data } : Leaf) : Types.Iter { - var i : Nat = 0; - object { - public func next() : ?T { - if (i >= data.count) { - null - } else { - let res = data.elements[i]; - i += 1; - res - } - } - } - }; - - func leafElementsFrom({ data } : Leaf, compare : (T, T) -> Order.Order, element : T) : Types.Iter { - var i = switch (BinarySearch.binarySearchNode(data.elements, compare, element, data.count)) { - case (#elementFound(i)) i; - case (#notFound(i)) i - }; - object { - public func next() : ?T { - if (i >= data.count) { - null - } else { - let res = data.elements[i]; - i += 1; - res - } - } - } - }; - - func reverseLeafElements({ data } : Leaf) : Types.Iter { - var i : Nat = data.count; - object { - public func next() : ?T { - if (i == 0) { - null - } else { - let res = data.elements[i - 1]; - i -= 1; - res - } - } - } - }; - - func reverseLeafElementsFrom({ data } : Leaf, compare : (T, T) -> Order.Order, element : T) : Types.Iter { - var i = switch (BinarySearch.binarySearchNode(data.elements, compare, element, data.count)) { - case (#elementFound(i)) i + 1; // +1 to include this element - case (#notFound(i)) i // i is the index of the first element greater than the search element, or count if all elements are less than the search element - }; - object { - public func next() : ?T { - if (i == 0) { - null - } else { - let res = data.elements[i - 1]; - i -= 1; - res - } - } - } - }; - - // Cursor type that keeps track of the current node and the current element index in the node - type NodeCursor = { node : Node; elementIndex : Nat }; - - func internalElements(internal : Internal) : Types.Iter { - // The nodeCursorStack keeps track of the current node and the current element index in the node - // We use a stack here to push to/pop off the next node cursor to visit - let nodeCursorStack = initializeForwardNodeCursorStack(internal); - internalElementsFromStack(nodeCursorStack) - }; - - func internalElementsFrom(internal : Internal, compare : (T, T) -> Order.Order, element : T) : Types.Iter { - let nodeCursorStack = initializeForwardNodeCursorStackFrom(internal, compare, element); - internalElementsFromStack(nodeCursorStack) - }; - - func internalElementsFromStack(nodeCursorStack : Stack.Stack>) : Types.Iter { - object { - public func next() : ?T { - // pop the next node cursor off the stack - var nodeCursor = Stack.pop(nodeCursorStack); - switch (nodeCursor) { - case null { return null }; - case (?{ node; elementIndex }) { - switch (node) { - // if a leaf node, iterate through the leaf node's next element - case (#leaf(leafNode)) { - let lastIndex = leafNode.data.count - 1 : Nat; - if (elementIndex > lastIndex) { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalElements(), leaf elementIndex out of bounds") - }; - - let currentElement = switch (leafNode.data.elements[elementIndex]) { - case (?element) { element }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Set.internalElements(), null element found in leaf node." - # "leafNode.data.count=" # debug_show (leafNode.data.count) # ", elementIndex=" # debug_show (elementIndex) - ) - } - }; - // if not at the last element, push the next element index of the leaf onto the stack and return the current element - if (elementIndex < lastIndex) { - Stack.push( - nodeCursorStack, - { - node = #leaf(leafNode); - elementIndex = elementIndex + 1 : Nat - } - ) - }; - - ?currentElement - }; - // if an internal node - case (#internal(internalNode)) { - let lastIndex = internalNode.data.count - 1 : Nat; - // Developer facing message in case of a bug - if (elementIndex > lastIndex) { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalElements(), internal elementIndex out of bounds") - }; - - let currentElement = switch (internalNode.data.elements[elementIndex]) { - case (?element) { element }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Set.internalElements(), null element found in internal node. " # - "internal.data.count=" # debug_show (internalNode.data.count) # ", elementIndex=" # debug_show (elementIndex) - ) - } - }; - - let nextCursor = { - node = #internal(internalNode); - elementIndex = elementIndex + 1 : Nat - }; - // if not the last element, push the next element of the internal node onto the stack - if (elementIndex < lastIndex) { - Stack.push(nodeCursorStack, nextCursor) - }; - // traverse the next child's min subtree and push the resulting node cursors onto the stack - // then return the current element of the internal node - traverseMinSubtreeIter(nodeCursorStack, nextCursor); - ?currentElement - } - } - } - } - } - } - }; - - func reverseInternalElements(internal : Internal) : Types.Iter { - // The nodeCursorStack keeps track of the current node and the current element index in the node - // We use a stack here to push to/pop off the next node cursor to visit - let nodeCursorStack = initializeReverseNodeCursorStack(internal); - reverseInternalElementsFromStack(nodeCursorStack) - }; - - func reverseInternalElementsFrom(internal : Internal, compare : (T, T) -> Order.Order, element : T) : Types.Iter { - let nodeCursorStack = initializeReverseNodeCursorStackFrom(internal, compare, element); - reverseInternalElementsFromStack(nodeCursorStack) - }; - - func reverseInternalElementsFromStack(nodeCursorStack : Stack.Stack>) : Types.Iter { - object { - public func next() : ?T { - // pop the next node cursor off the stack - var nodeCursor = Stack.pop(nodeCursorStack); - switch (nodeCursor) { - case null { return null }; - case (?{ node; elementIndex }) { - let firstIndex = 0 : Nat; - assert (elementIndex > firstIndex); - switch (node) { - // if a leaf node, reverse iterate through the leaf node's next element - case (#leaf(leafNode)) { - let currentElement = switch (leafNode.data.elements[elementIndex - 1]) { - case (?element) { element }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Set.reverseInternalElements(), null element found in leaf node." - # "leafNode.data.count=" # debug_show (leafNode.data.count) # ", elementIndex=" # debug_show (elementIndex) - ) - } - }; - // if not at the last element, push the previous element index of the leaf onto the stack and return the current element - if (elementIndex - 1 : Nat > firstIndex) { - Stack.push( - nodeCursorStack, - { - node = #leaf(leafNode); - elementIndex = elementIndex - 1 : Nat - } - ) - }; - - // return the current element - ?currentElement - }; - // if an internal node - case (#internal(internalNode)) { - let currentElement = switch (internalNode.data.elements[elementIndex - 1]) { - case (?element) { element }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Set.reverseInternalElements(), null element found in internal node. " # - "internal.data.count=" # debug_show (internalNode.data.count) # ", elementIndex=" # debug_show (elementIndex) - ) - } - }; - - let previousCursor = { - node = #internal(internalNode); - elementIndex = elementIndex - 1 : Nat - }; - // if not the first element, push the previous element index of the internal node onto the stack - if (elementIndex - 1 : Nat > firstIndex) { - Stack.push(nodeCursorStack, previousCursor) - }; - // traverse the previous child's max subtree and push the resulting node cursors onto the stack - // then return the current element of the internal node - traverseMaxSubtreeIter(nodeCursorStack, previousCursor); - ?currentElement - } - } - } - } - } - } - }; - - func initializeForwardNodeCursorStack(internal : Internal) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - elementIndex = 0 - }; - - // push the initial cursor to the stack - Stack.push(nodeCursorStack, nodeCursor); - // then traverse left - traverseMinSubtreeIter(nodeCursorStack, nodeCursor); - nodeCursorStack - }; - - func initializeForwardNodeCursorStackFrom(internal : Internal, compare : (T, T) -> Order.Order, element : T) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - elementIndex = 0 - }; - - traverseMinSubtreeIterFrom(nodeCursorStack, nodeCursor, compare, element); - nodeCursorStack - }; - - func initializeReverseNodeCursorStack(internal : Internal) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - elementIndex = internal.data.count - }; - - // push the initial cursor to the stack - Stack.push(nodeCursorStack, nodeCursor); - // then traverse left - traverseMaxSubtreeIter(nodeCursorStack, nodeCursor); - nodeCursorStack - }; - - func initializeReverseNodeCursorStackFrom(internal : Internal, compare : (T, T) -> Order.Order, element : T) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - elementIndex = internal.data.count - }; - - traverseMaxSubtreeIterFrom(nodeCursorStack, nodeCursor, compare, element); - nodeCursorStack - }; - - // traverse the min subtree of the current node cursor, passing each new element to the node cursor stack - func traverseMinSubtreeIter(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor) { - var currentNode = nodeCursor.node; - var childIndex = nodeCursor.elementIndex; - - label l loop { - switch (currentNode) { - // If currentNode is leaf, have hit the minimum element of the subtree and already pushed it's cursor to the stack - // so can return - case (#leaf(_)) { - return - }; - // If currentNode is internal, add it's left most child to the stack and continue traversing - case (#internal(internalNode)) { - switch (internalNode.children[childIndex]) { - // Push the next min (left most) child node to the stack - case (?childNode) { - childIndex := 0; - currentNode := childNode; - Stack.push( - nodeCursorStack, - { - node = currentNode; - elementIndex = childIndex - } - ) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.traverseMinSubtreeIter(), null child node error") - } - } - } - } - } - }; - - func traverseMinSubtreeIterFrom(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor, compare : (T, T) -> Order.Order, element : T) { - var currentNode = nodeCursor.node; - - label l loop { - let (node, childrenOption) = switch (currentNode) { - case (#leaf(leafNode)) (leafNode, null); - case (#internal(internalNode)) (internalNode, ?internalNode.children) - }; - let (i, isFound) = switch (NodeUtil.getElementIndex(node.data, compare, element)) { - case (#elementFound(i)) (i, true); - case (#notFound(i)) (i, false) - }; - if (i < node.data.count) { - Stack.push( - nodeCursorStack, - { - node = currentNode; - elementIndex = i // greater elements to traverse - } - ) - }; - if isFound return; - let ?children = childrenOption else return; - let ?childNode = children[i] else Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.traverseMinSubtreeIterFrom(), null child node error"); - currentNode := childNode - } - }; - - // traverse the max subtree of the current node cursor, passing each new element to the node cursor stack - func traverseMaxSubtreeIter(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor) { - var currentNode = nodeCursor.node; - var childIndex = nodeCursor.elementIndex; - - label l loop { - switch (currentNode) { - // If currentNode is leaf, have hit the maximum element of the subtree and already pushed it's cursor to the stack - // so can return - case (#leaf(_)) { - return - }; - // If currentNode is internal, add it's right most child to the stack and continue traversing - case (#internal(internalNode)) { - assert (childIndex <= internalNode.data.count); // children are one more than data elements - switch (internalNode.children[childIndex]) { - // Push the next max (right most) child node to the stack - case (?childNode) { - childIndex := switch (childNode) { - case (#internal(internalNode)) internalNode.data.count; - case (#leaf(leafNode)) leafNode.data.count - }; - currentNode := childNode; - Stack.push( - nodeCursorStack, - { - node = currentNode; - elementIndex = childIndex - } - ) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.traverseMaxSubtreeIter(), null child node error") - } - } - } - } - } - }; - - func traverseMaxSubtreeIterFrom(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor, compare : (T, T) -> Order.Order, element : T) { - var currentNode = nodeCursor.node; - - label l loop { - let (node, childrenOption) = switch (currentNode) { - case (#leaf(leafNode)) (leafNode, null); - case (#internal(internalNode)) (internalNode, ?internalNode.children) - }; - let (i, isFound) = switch (NodeUtil.getElementIndex(node.data, compare, element)) { - case (#elementFound(i)) (i + 1, true); // +1 to include this element - case (#notFound(i)) (i, false) // i is the index of the first element less than the search element, or 0 if all elements are greater than the search element - }; - if (i > 0) { - Stack.push( - nodeCursorStack, - { - node = currentNode; - elementIndex = i - } - ) - }; - if isFound return; - let ?children = childrenOption else return; - let ?childNode = children[i] else Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.traverseMaxSubtreeIterFrom(), null child node error"); - currentNode := childNode - } - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateInternalDeleteResult = { - // element was deleted - #deleted; - // element was absent - #inexistent; - // deleted an element, but was unable to successfully borrow and rebalance at the previous level without merging children - // the internalChild is the merged child that needs to be rebalanced at the next level up in the BTree - #mergeChild : { - internalChild : Internal - } - }; - - func internalDeleteHelper(internalNode : Internal, order : Nat, compare : (T, T) -> Order.Order, deleteElement : T, skipNode : Bool) : IntermediateInternalDeleteResult { - let minElements = NodeUtil.minElementsFromOrder(order); - let elementIndex = NodeUtil.getElementIndex(internalNode.data, compare, deleteElement); - - // match on both the result of the node binary search, and if this node level should be skipped even if the element is found (internal element replacement case) - switch (elementIndex, skipNode) { - // if element is found in the internal node - case (#elementFound(deleteIndex), false) { - if (Option.isNull(internalNode.data.elements[deleteIndex])) { - Runtime.trap("Bug in Set.internalDeleteHelper") - }; - // TODO: (optimization) replace with deletion in one step without having to retrieve the max element first - let replaceElement = NodeUtil.getMaxElement(internalNode.children[deleteIndex]); - internalNode.data.elements[deleteIndex] := ?replaceElement; - switch (internalDeleteHelper(internalNode, order, compare, replaceElement, true)) { - case (#deleted) { #deleted }; - case (#inexistent) { #inexistent }; - case (#mergeChild({ internalChild })) { - #mergeChild({ internalChild }) - } - } - }; - // if element is not found in the internal node OR the element is found, but skipping this node (because deleting the in order precessor i.e. replacement element) - // in both cases need to descend and traverse to find the element to delete - case ((#elementFound(_), true) or (#notFound(_), _)) { - let childIndex = switch (elementIndex) { - case (#elementFound(replacedSkipElementIndex)) { - replacedSkipElementIndex - }; - case (#notFound(childIndex)) { childIndex } - }; - let child = switch (internalNode.children[childIndex]) { - case (?c) { c }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalDeleteHelper, child index of #elementFound or #notfound is null") - } - }; - switch (child) { - // if child is internal - case (#internal(internalChild)) { - switch (internalDeleteHelper(internalChild, order, compare, deleteElement, false), childIndex == 0) { - // if element was successfully deleted and no additional tree re-balancing is needed, return #deleted - case (#deleted, _) { #deleted }; - case (#inexistent, _) { #inexistent }; - // if internalChild needs rebalancing and pulling child is left most - case (#mergeChild({ internalChild }), true) { - // try to pull left-most element and child from right sibling - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex + 1, #successor)) { - // if can pull up sibling element and child - case (#borrowed({ deletedSiblingElement; child })) { - NodeUtil.rotateBorrowedElementsAndChildFromSibling( - internalNode, - childIndex, - deletedSiblingElement, - child, - internalChild, - #right - ); - #deleted - }; - // unable to pull from sibling, need to merge with right sibling and push down parent - case (#notEnoughElements(sibling)) { - // get the parent element that will be pushed down the the child - let elementsToBePushedToChild = ?BTreeHelper.deleteAndShift(internalNode.data.elements, 0); - internalNode.data.count -= 1; - // merge the children and push down the parent - let newChild = NodeUtil.mergeChildrenAndPushDownParent(internalChild, elementsToBePushedToChild, sibling); - // update children of the parent - internalNode.children[0] := ?#internal(newChild); - ignore ?BTreeHelper.deleteAndShift(internalNode.children, 1); - - if (internalNode.data.count < minElements) { - #mergeChild({ internalChild = internalNode }) - } else { - #deleted - } - } - } - }; - // if internalChild needs rebalancing and pulling child is > 0, so a left sibling exists - case (#mergeChild({ internalChild }), false) { - // try to pull right-most element and its child directly from left sibling - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex - 1 : Nat, #predecessor)) { - case (#borrowed({ deletedSiblingElement; child })) { - NodeUtil.rotateBorrowedElementsAndChildFromSibling( - internalNode, - childIndex - 1 : Nat, - deletedSiblingElement, - child, - internalChild, - #left - ); - #deleted - }; - // unable to pull from left sibling - case (#notEnoughElements(leftSibling)) { - // if child is not last index, try to pull from the right child - if (childIndex < internalNode.data.count) { - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex, #successor)) { - // if can pull up sibling element and child - case (#borrowed({ deletedSiblingElement; child })) { - NodeUtil.rotateBorrowedElementsAndChildFromSibling( - internalNode, - childIndex, - deletedSiblingElement, - child, - internalChild, - #right - ); - return #deleted - }; - // if cannot borrow, from left or right, merge (see below) - case _ {} - } - }; - - // get the parent element that will be pushed down the the child - let elementToBePushedToChild = ?BTreeHelper.deleteAndShift(internalNode.data.elements, childIndex - 1 : Nat); - internalNode.data.count -= 1; - // merge it the children and push down the parent - let newChild = NodeUtil.mergeChildrenAndPushDownParent(leftSibling, elementToBePushedToChild, internalChild); - - // update children of the parent - internalNode.children[childIndex - 1] := ?#internal(newChild); - ignore ?BTreeHelper.deleteAndShift(internalNode.children, childIndex); - - if (internalNode.data.count < minElements) { - #mergeChild({ internalChild = internalNode }) - } else { - #deleted - } - } - } - } - } - }; - // if child is leaf - case (#leaf(leafChild)) { - switch (leafDeleteHelper(leafChild, order, compare, deleteElement), childIndex == 0) { - case (#deleted, _) { #deleted }; - case (#inexistent, _) { #inexistent }; - // if delete child is left most, try to borrow from right child - case (#mergeLeafData({ leafDeleteIndex }), true) { - switch (NodeUtil.borrowFromRightLeafChild(internalNode.children, childIndex)) { - case (?borrowedElement) { - let elementToBePushedToChild = internalNode.data.elements[childIndex]; - internalNode.data.elements[childIndex] := ?borrowedElement; - - ignore BTreeHelper.insertAtPostionAndDeleteAtPosition(leafChild.data.elements, elementToBePushedToChild, leafChild.data.count - 1, leafDeleteIndex); - #deleted - }; - - case null { - // can't borrow from right child, delete from leaf and merge with right child and parent element, then push down into new leaf - let rightChild = switch (internalNode.children[childIndex + 1]) { - case (?#leaf(rc)) { rc }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalDeleteHelper, if trying to borrow from right leaf child is null, rightChild index cannot be null or internal") - } - }; - let mergedLeaf = mergeParentWithLeftRightChildLeafNodesAndDelete( - internalNode.data.elements[childIndex], - leafChild, - rightChild, - leafDeleteIndex, - #left - ); - // delete the left most internal node element, since was merging from a deletion in left most child (0) and the parent element was pushed into the mergedLeaf - ignore BTreeHelper.deleteAndShift(internalNode.data.elements, 0); - // update internal node children - BTreeHelper.replaceTwoWithElementAndShift>(internalNode.children, #leaf(mergedLeaf), 0); - internalNode.data.count -= 1; - - if (internalNode.data.count < minElements) { - #mergeChild({ - internalChild = internalNode - }) - } else { - #deleted - } - - } - } - }; - // if delete child is middle or right most, try to borrow from left child - case (#mergeLeafData({ leafDeleteIndex }), false) { - // if delete child is right most, try to borrow from left child - switch (NodeUtil.borrowFromLeftLeafChild(internalNode.children, childIndex)) { - case (?borrowedElement) { - let elementToBePushedToChild = internalNode.data.elements[childIndex - 1]; - internalNode.data.elements[childIndex - 1] := ?borrowedElement; - ignore BTreeHelper.insertAtPostionAndDeleteAtPosition(leafChild.data.elements, elementToBePushedToChild, 0, leafDeleteIndex); - #deleted - }; - case null { - // if delete child is in the middle, try to borrow from right child - if (childIndex < internalNode.data.count) { - // try to borrow from right - switch (NodeUtil.borrowFromRightLeafChild(internalNode.children, childIndex)) { - case (?borrowedElement) { - let elementToBePushedToChild = internalNode.data.elements[childIndex]; - internalNode.data.elements[childIndex] := ?borrowedElement; - // insert the successor at the very last element - ignore BTreeHelper.insertAtPostionAndDeleteAtPosition(leafChild.data.elements, elementToBePushedToChild, leafChild.data.count - 1, leafDeleteIndex); - return #deleted - }; - // if cannot borrow, from left or right, merge (see below) - case _ {} - } - }; - - // can't borrow from left child, delete from leaf and merge with left child and parent element, then push down into new leaf - let leftChild = switch (internalNode.children[childIndex - 1]) { - case (?#leaf(lc)) { lc }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalDeleteHelper, if trying to borrow from left leaf child is null, then left child index must not be null or internal") - } - }; - let mergedLeaf = mergeParentWithLeftRightChildLeafNodesAndDelete( - internalNode.data.elements[childIndex - 1], - leftChild, - leafChild, - leafDeleteIndex, - #right - ); - // delete the right most internal node element, since was merging from a deletion in the right most child and the parent element was pushed into the mergedLeaf - ignore BTreeHelper.deleteAndShift(internalNode.data.elements, childIndex - 1); - // update internal node children - BTreeHelper.replaceTwoWithElementAndShift>(internalNode.children, #leaf(mergedLeaf), childIndex - 1); - internalNode.data.count -= 1; - - if (internalNode.data.count < minElements) { - #mergeChild({ - internalChild = internalNode - }) - } else { - #deleted - } - } - } - } - } - } - } - } - } - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateLeafDeleteResult = { - // element was deleted - #deleted; - // element was absent - #inexistent; - // leaf had the minimum number of elements when deleting, so returns the leaf node's data and the index of the element that will be deleted - #mergeLeafData : { - data : Data; - leafDeleteIndex : Nat - } - }; - - func leafDeleteHelper(leafNode : Leaf, order : Nat, compare : (T, T) -> Order.Order, deleteElement : T) : IntermediateLeafDeleteResult { - let minElements = NodeUtil.minElementsFromOrder(order); - - switch (NodeUtil.getElementIndex(leafNode.data, compare, deleteElement)) { - case (#elementFound(deleteIndex)) { - if (leafNode.data.count > minElements) { - leafNode.data.count -= 1; - ignore BTreeHelper.deleteAndShift(leafNode.data.elements, deleteIndex); - #deleted - } else { - #mergeLeafData({ - data = leafNode.data; - leafDeleteIndex = deleteIndex - }) - } - }; - case (#notFound(_)) { - #inexistent - } - } - }; - - func containsInInternal(internalNode : Internal, compare : (T, T) -> Order.Order, element : T) : Bool { - switch (NodeUtil.getElementIndex(internalNode.data, compare, element)) { - case (#elementFound _index) { - true - }; - case (#notFound(index)) { - switch (internalNode.children[index]) { - // expects the child to be there, otherwise there's a bug in binary search or the tree is invalid - case null { Runtime.trap("Internal bug: Set.containsInInternal") }; - case (?#leaf(leafNode)) { containsInLeaf(leafNode, compare, element) }; - case (?#internal(internalNode)) { - containsInInternal(internalNode, compare, element) - } - } - } - } - }; - - func containsInLeaf(leafNode : Leaf, compare : (T, T) -> Order.Order, element : T) : Bool { - switch (NodeUtil.getElementIndex(leafNode.data, compare, element)) { - case (#elementFound(_index)) { - true - }; - case _ false - } - }; - - type DeletionSide = { #left; #right }; - - func mergeParentWithLeftRightChildLeafNodesAndDelete( - parentElement : ?T, - leftChild : Leaf, - rightChild : Leaf, - deleteIndex : Nat, - deletionSide : DeletionSide - ) : Leaf { - let count = leftChild.data.count * 2; - let (elements, _) = BTreeHelper.mergeParentWithChildrenAndDelete( - parentElement, - leftChild.data.count, - leftChild.data.elements, - rightChild.data.elements, - deleteIndex, - deletionSide - ); - ({ - data = { - elements; - var count = count - } - }) - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateInsertResult = { - // element was inserted - #inserted; - // element was alreay present - #existent; - // child was full when inserting, so returns the promoted element and the split left and right child - #promote : { - element : T; - leftChild : Node; - rightChild : Node - } - }; - - // Helper for inserting into a leaf node - func leafInsertHelper(leafNode : Leaf, order : Nat, compare : (T, T) -> Order.Order, insertedElement : T) : (IntermediateInsertResult) { - // Perform binary search to see if the element exists in the node - switch (NodeUtil.getElementIndex(leafNode.data, compare, insertedElement)) { - case (#elementFound(insertIndex)) { - let previous = leafNode.data.elements[insertIndex]; - leafNode.data.elements[insertIndex] := ?insertedElement; - switch (previous) { - case (?_) { #existent }; - case null { Runtime.trap("Bug in Set.leafInsertHelper") }; // the binary search already found an element, so this case should never happen - } - }; - case (#notFound(insertIndex)) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - let maxElements : Nat = order - 1; - // If the leaf is full, insert, split the node, and promote the middle element - if (leafNode.data.count >= maxElements) { - let (leftElements, promotedParentElement, rightElements) = BTreeHelper.insertOneAtIndexAndSplitArray( - leafNode.data.elements, - insertedElement, - insertIndex - ); - - let leftCount = order / 2; - let rightCount : Nat = if (order % 2 == 0) { leftCount - 1 } else { - leftCount - }; - - ( - #promote({ - element = promotedParentElement; - leftChild = createLeaf(leftElements, leftCount); - rightChild = createLeaf(rightElements, rightCount) - }) - ) - } - // Otherwise, insert at the specified index (shifting elements over if necessary) - else { - NodeUtil.insertAtIndexOfNonFullNodeData(leafNode.data, ?insertedElement, insertIndex); - #inserted - } - } - } - }; - - // Helper for inserting into an internal node - func internalInsertHelper(internalNode : Internal, order : Nat, compare : (T, T) -> Order.Order, insertElement : T) : IntermediateInsertResult { - switch (NodeUtil.getElementIndex(internalNode.data, compare, insertElement)) { - case (#elementFound(insertIndex)) { - let previous = internalNode.data.elements[insertIndex]; - internalNode.data.elements[insertIndex] := ?insertElement; - switch (previous) { - case (?_) { #existent }; - case null { - Runtime.trap("Bug in Set.internalInsertHelper, element found") - }; // the binary search already found an element, so this case should never happen - } - }; - case (#notFound(insertIndex)) { - let insertResult = switch (internalNode.children[insertIndex]) { - case null { - Runtime.trap("Bug in Set.internalInsertHelper, not found") - }; - case (?#leaf(leafNode)) { - leafInsertHelper(leafNode, order, compare, insertElement) - }; - case (?#internal(internalChildNode)) { - internalInsertHelper(internalChildNode, order, compare, insertElement) - } - }; - - switch (insertResult) { - case (#inserted) #inserted; - case (#existent) #existent; - case (#promote({ element = promotedElement; leftChild; rightChild })) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - let maxElements : Nat = order - 1; - // if current internal node is full, need to split the internal node - if (internalNode.data.count >= maxElements) { - // insert and split internal elements, determine new promotion target element - let (leftElements, promotedParentElement, rightElements) = BTreeHelper.insertOneAtIndexAndSplitArray( - internalNode.data.elements, - promotedElement, - insertIndex - ); - - // calculate the element count in the left elements and the element count in the right elements - let leftCount = order / 2; - let rightCount : Nat = if (order % 2 == 0) { leftCount - 1 } else { - leftCount - }; - - // split internal children - let (leftChildren, rightChildren) = NodeUtil.splitChildrenInTwoWithRebalances( - internalNode.children, - insertIndex, - leftChild, - rightChild - ); - - // send the element to be promoted, as well as the internal children left and right split - #promote({ - element = promotedParentElement; - leftChild = #internal({ - data = { elements = leftElements; var count = leftCount }; - children = leftChildren - }); - rightChild = #internal({ - data = { elements = rightElements; var count = rightCount }; - children = rightChildren - }) - }) - } else { - // insert the new elements into the internal node - NodeUtil.insertAtIndexOfNonFullNodeData(internalNode.data, ?promotedElement, insertIndex); - // split and re-insert the single child that needs rebalancing - NodeUtil.insertRebalancedChild(internalNode.children, insertIndex, leftChild, rightChild); - #inserted - } - } - } - } - } - }; - - func createLeaf(elements : [var ?T], count : Nat) : Node { - #leaf({ - data = { - elements; - var count - } - }) - }; - - // FIXME - // Additional functionality compared to original source. - - func cloneData(data : Data) : Data { - { - elements = VarArray.clone(data.elements); - var count = data.count - } - }; - - func cloneNode(node : Node) : Node { - switch node { - case (#leaf { data }) { - #leaf { data = cloneData(data) } - }; - case (#internal { data; children }) { - let clonedData = cloneData(data); - let clonedChildren = VarArray.map, ?Node>( - children, - func child { - switch child { - case null null; - case (?childNode) ?cloneNode(childNode) - } - } - ); - #internal({ - data = clonedData; - children = clonedChildren - }) - } - } - }; - - module BinarySearch { - public type SearchResult = { - #elementFound : Nat; - #notFound : Nat - }; - - /// Searches an array for a specific element, returning the index it occurs at if #elementFound, or the child/insert index it may occur at - /// if #notFound. This is used when determining if a element exists in an internal or leaf node, where an element should be inserted in a - /// leaf node, or which child of an internal node a element could be in. - /// - /// Note: This function expects a mutable, nullable, array of elements in sorted order, where all nulls appear at the end of the array. - /// This function may trap if a null element appears before any elements. It also expects a maxIndex, which is the right-most index (bound) - /// from which to begin the binary search (the left most bound is expected to be 0) - /// - /// Parameters: - /// - /// * array - the sorted array that the binary search is performed upon - /// * compare - the comparator used to perform the search - /// * searchElement - the element being compared against in the search - /// * maxIndex - the right-most index (bound) from which to begin the search - public func binarySearchNode(array : [var ?T], compare : (T, T) -> Order.Order, searchElement : T, maxIndex : Nat) : SearchResult { - // TODO: get rid of this check? - // Trap if array is size 0 (should not happen) - if (array.size() == 0) { - assert false - }; - - // if all elements in the array are null (i.e. first element is null), return #notFound(0) - if (maxIndex == 0) { - return #notFound(0) - }; - - // Initialize search from first to last index - var left : Nat = 0; - var right = maxIndex; // maxIndex does not necessarily mean array.size() - 1 - // Search the array - while (left < right) { - let middle = (left + right) / 2; - switch (array[middle]) { - case null { assert false }; - case (?element) { - switch (compare(searchElement, element)) { - // If the element is present at the middle itself - case (#equal) { return #elementFound(middle) }; - // If element is greater than mid, it can only be present in left subarray - case (#greater) { left := middle + 1 }; - // If element is smaller than mid, it can only be present in right subarray - case (#less) { - right := if (middle == 0) { 0 } else { middle - 1 } - } - } - } - } - }; - - if (left == array.size()) { - return #notFound(left) - }; - - // left == right - switch (array[left]) { - // inserting at end of array - case null { #notFound(left) }; - case (?element) { - switch (compare(searchElement, element)) { - // if left is the searched element - case (#equal) { #elementFound(left) }; - // if the element is not found, return notFound and the insert location - case (#greater) { #notFound(left + 1) }; - case (#less) { #notFound(left) } - } - } - } - } - }; - - module NodeUtil { - /// Inserts element at the given index into a non-full leaf node - public func insertAtIndexOfNonFullNodeData(data : Data, element : ?T, insertIndex : Nat) { - let currentLastElementIndex : Nat = if (data.count == 0) { 0 } else { - data.count - 1 - }; - BTreeHelper.insertAtPosition(data.elements, element, insertIndex, currentLastElementIndex); - - // increment the count of data in this node since just inserted an element - data.count += 1 - }; - - /// Inserts two rebalanced (split) child halves into a non-full array of children. - public func insertRebalancedChild(children : [var ?Node], rebalancedChildIndex : Nat, leftChildInsert : Node, rightChildInsert : Node) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - var j : Nat = children.size() - 2; - - // This is just a sanity check to ensure the children aren't already full (should split promote otherwise) - // TODO: Remove this check once confident - if (Option.isSome(children[j + 1])) { assert false }; - - // Iterate backwards over the array and shift each element over to the right by one until the rebalancedChildIndex is hit - while (j > rebalancedChildIndex) { - children[j + 1] := children[j]; - j -= 1 - }; - - // Insert both the left and right rebalanced children (replacing the pre-split child) - children[j] := ?leftChildInsert; - children[j + 1] := ?rightChildInsert - }; - - /// Used when splitting the children of an internal node - /// - /// Takes in the rebalanced child index, as well as both halves of the rebalanced child and splits the children, inserting the left and right child halves appropriately - /// - /// For more context, see the documentation for the splitArrayAndInsertTwo method in ArrayUtils.mo - public func splitChildrenInTwoWithRebalances( - children : [var ?Node], - rebalancedChildIndex : Nat, - leftChildInsert : Node, - rightChildInsert : Node - ) : ([var ?Node], [var ?Node]) { - BTreeHelper.splitArrayAndInsertTwo>(children, rebalancedChildIndex, leftChildInsert, rightChildInsert) - }; - - /// Helper used to get the element index of of a element within a node - /// - /// for more, see the BinarySearch.binarySearchNode() documentation - public func getElementIndex(data : Data, compare : (T, T) -> Order.Order, element : T) : BinarySearch.SearchResult { - BinarySearch.binarySearchNode(data.elements, compare, element, data.count) - }; - - // calculates a BTree Node's minimum allowed elements given the order of the BTree - public func minElementsFromOrder(order : Nat) : Nat { - if (order % 2 == 0) { order / 2 - 1 } else { order / 2 } - }; - - // Given a node, get the maximum element (right most leaf element) - public func getMaxElement(node : ?Node) : T { - switch (node) { - case (?#leaf({ data })) { - switch (data.elements[data.count - 1]) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.NodeUtil.getMaxElement, data cannot have more elements than it's count") - }; - case (?element) { element } - } - }; - case (?#internal({ data; children })) { - getMaxElement(children[data.count]) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.NodeUtil.getMaxElement, the node provided cannot be null") - } - } - }; - - type InorderBorrowType = { - #predecessor; - #successor - }; - - // attempts to retrieve the in max element of the child leaf node directly to the left if the node will allow it - // returns the deleted max element if able to retrieve, null if not able - // - // mutates the predecessing node's elements - public func borrowFromLeftLeafChild(children : [var ?Node], ofChildIndex : Nat) : ?T { - let predecessorIndex : Nat = ofChildIndex - 1; - borrowFromLeafChild(children, predecessorIndex, #predecessor) - }; - - // attempts to retrieve the in max element of the child leaf node directly to the right if the node will allow it - // returns the deleted max element if able to retrieve, null if not able - // - // mutates the predecessing node's elements - public func borrowFromRightLeafChild(children : [var ?Node], ofChildIndex : Nat) : ?T { - borrowFromLeafChild(children, ofChildIndex + 1, #successor) - }; - - func borrowFromLeafChild(children : [var ?Node], borrowChildIndex : Nat, childSide : InorderBorrowType) : ?T { - let minElements = minElementsFromOrder(children.size()); - - switch (children[borrowChildIndex]) { - case (?#leaf({ data })) { - if (data.count > minElements) { - // able to borrow an element from this child, so decrement the count of elements - data.count -= 1; // Since enforce order >= 4, there will always be at least 1 element per node - switch (childSide) { - case (#predecessor) { - let deletedElement = data.elements[data.count]; - data.elements[data.count] := null; - deletedElement - }; - case (#successor) { - ?BTreeHelper.deleteAndShift(data.elements, 0) - } - } - } else { null } - }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.NodeUtil.borrowFromLeafChild, the node at the borrow child index cannot be null or internal") - } - } - }; - - type InternalBorrowResult = { - #borrowed : InternalBorrow; - #notEnoughElements : Internal - }; - - type InternalBorrow = { - deletedSiblingElement : ?T; - child : ?Node - }; - - // Attempts to borrow an element and child from an internal sibling node - public func borrowFromInternalSibling(children : [var ?Node], borrowChildIndex : Nat, borrowType : InorderBorrowType) : InternalBorrowResult { - let minElements = minElementsFromOrder(children.size()); - - switch (children[borrowChildIndex]) { - case (?#internal({ data; children })) { - if (data.count > minElements) { - data.count -= 1; - switch (borrowType) { - case (#predecessor) { - let deletedSiblingElement = data.elements[data.count]; - data.elements[data.count] := null; - let child = children[data.count + 1]; - children[data.count + 1] := null; - #borrowed({ - deletedSiblingElement; - child - }) - }; - case (#successor) { - #borrowed({ - deletedSiblingElement = ?BTreeHelper.deleteAndShift(data.elements, 0); - child = ?BTreeHelper.deleteAndShift(children, 0) - }) - } - } - } else { #notEnoughElements({ data; children }) } - }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.NodeUtil.borrowFromInternalSibling from internal sibling, the child at the borrow index cannot be null or a leaf") - } - } - }; - - type SiblingSide = { #left; #right }; - - // Rotates the borrowed elements and child from sibling side of the internal node to the internal child recipient - public func rotateBorrowedElementsAndChildFromSibling( - internalNode : Internal, - parentRotateIndex : Nat, - borrowedSiblingElement : ?T, - borrowedSiblingChild : ?Node, - internalChildRecipient : Internal, - siblingSide : SiblingSide - ) { - // if borrowing from the left, the rotated element and child will always be inserted first - // if borrowing from the right, the rotated element and child will always be inserted last - let (elementIndex, childIndex) = switch (siblingSide) { - case (#left) { (0, 0) }; - case (#right) { - (internalChildRecipient.data.count, internalChildRecipient.data.count + 1) - } - }; - - // get the parent element that will be pushed down the the child - let elementToBePushedToChild = internalNode.data.elements[parentRotateIndex]; - // replace the parent with the sibling element - internalNode.data.elements[parentRotateIndex] := borrowedSiblingElement; - // push the element and child down into the internalChild - insertAtIndexOfNonFullNodeData(internalChildRecipient.data, elementToBePushedToChild, elementIndex); - - BTreeHelper.insertAtPosition>(internalChildRecipient.children, borrowedSiblingChild, childIndex, internalChildRecipient.data.count) - }; - - // Merges the elements and children of two internal nodes, pushing the parent element in between the right and left halves - public func mergeChildrenAndPushDownParent(leftChild : Internal, parentElement : ?T, rightChild : Internal) : Internal { - { - data = mergeData(leftChild.data, parentElement, rightChild.data); - children = mergeChildren(leftChild.children, rightChild.children) - } - }; - - func mergeData(leftData : Data, parentElement : ?T, rightData : Data) : Data { - assert leftData.count <= minElementsFromOrder(leftData.elements.size() + 1); - assert rightData.count <= minElementsFromOrder(rightData.elements.size() + 1); - - let mergedElements = VarArray.repeat(null, leftData.elements.size()); - var i = 0; - while (i < leftData.count) { - mergedElements[i] := leftData.elements[i]; - i += 1 - }; - - mergedElements[i] := parentElement; - i += 1; - - var j = 0; - while (j < rightData.count) { - mergedElements[i] := rightData.elements[j]; - i += 1; - j += 1 - }; - - { - elements = mergedElements; - var count = leftData.count + 1 + rightData.count - } - }; - - func mergeChildren(leftChildren : [var ?Node], rightChildren : [var ?Node]) : [var ?Node] { - let mergedChildren = VarArray.repeat>(null, leftChildren.size()); - var i = 0; - - while (Option.isSome(leftChildren[i])) { - mergedChildren[i] := leftChildren[i]; - i += 1 - }; - - var j = 0; - while (Option.isSome(rightChildren[j])) { - mergedChildren[i] := rightChildren[j]; - i += 1; - j += 1 - }; - - mergedChildren - } - } -} diff --git a/.mops/core@2.3.1/src/Stack.mo b/.mops/core@2.3.1/src/Stack.mo deleted file mode 100644 index 89c099b..0000000 --- a/.mops/core@2.3.1/src/Stack.mo +++ /dev/null @@ -1,879 +0,0 @@ -/// A mutable stack data structure. -/// Elements can be pushed on top of the stack -/// and removed from top of the stack (LIFO). -/// -/// Example: -/// ```motoko -/// import Stack "mo:core/Stack"; -/// import Debug "mo:core/Debug"; -/// -/// persistent actor { -/// let levels = Stack.empty(); -/// Stack.push(levels, "Inner"); -/// Stack.push(levels, "Middle"); -/// Stack.push(levels, "Outer"); -/// assert Stack.pop(levels) == ?"Outer"; -/// assert Stack.pop(levels) == ?"Middle"; -/// assert Stack.pop(levels) == ?"Inner"; -/// assert Stack.pop(levels) == null; -/// } -/// ``` -/// -/// The internal implementation is a singly-linked list. -/// -/// Performance: -/// * Runtime: `O(1)` for push, pop, and peek operation. -/// * Space: `O(n)`. -/// `n` denotes the number of elements stored on the stack. - -// TODO: optimize or re-use pure/List operations (e.g. for `any` etc) - -import Order "Order"; -import Iter "Iter"; -import Types "Types"; -import PureList "pure/List"; - -module { - type List = Types.Pure.List; - public type Stack = Types.Stack; - - /// Convert a mutable stack to an immutable, purely functional list. - /// Please note that functional lists are ordered like stacks (FIFO). - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import PureList "mo:core/pure/List"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let mutableStack = Stack.empty(); - /// Stack.push(mutableStack, 3); - /// Stack.push(mutableStack, 2); - /// Stack.push(mutableStack, 1); - /// let immutableList = Stack.toPure(mutableStack); - /// assert Iter.toArray(PureList.values(immutableList)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the stack. - /// @deprecated M0235 - public func toPure(self : Stack) : PureList.List { - self.top - }; - - public func toArray(self : Stack) : [T] { - Iter.toArray(values(self)) - }; - - public func toVarArray(self : Stack) : [var T] { - Iter.toVarArray(values(self)) - }; - - /// Convert an immutable, purely functional list to a mutable stack. - /// Please note that functional lists are ordered like stacks (FIFO). - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import PureList "mo:core/pure/List"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let immutableList = PureList.fromIter([1, 2, 3].values()); - /// let mutableStack = Stack.fromPure(immutableList); - /// assert Iter.toArray(Stack.values(mutableStack)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the queue. - /// @deprecated M0235 - public func fromPure(list : PureList.List) : Stack { - var size = 0; - var cur = list; - loop { - switch cur { - case (?(_, next)) { - size += 1; - cur := next - }; - case null { - return { var top = list; var size } - } - } - } - }; - - public func fromVarArray(array : [var T]) : Stack { - fromIter(array.values()) - }; - - public func fromArray(array : [T]) : Stack { - fromIter(array.values()) - }; - - /// Create a new empty mutable stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// assert Stack.size(stack) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Stack { - { - var top = null; - var size = 0 - } - }; - - /// Creates a new stack with `size` elements by applying the `generator` function to indices `[0..size-1]`. - /// Elements are pushed in ascending index order. - /// Which means that the generated element with the index `0` will be at the bottom of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let stack = Stack.tabulate(3, func(i) { 2 * i }); - /// assert Iter.toArray(Stack.values(stack)) == [4, 2, 0]; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `generator` has O(1) costs. - public func tabulate(size : Nat, generator : Nat -> T) : Stack { - let stack = empty(); - var index = 0; - while (index < size) { - let element = generator(index); - push(stack, element); - index += 1 - }; - stack - }; - - /// Creates a new stack containing a single element. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.singleton("motoko"); - /// assert Stack.peek(stack) == ?"motoko"; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func singleton(element : T) : Stack { - let stack = empty(); - push(stack, element); - stack - }; - - /// Removes all elements from the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// Stack.clear(stack); - /// assert Stack.isEmpty(stack); - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func clear(self : Stack) { - self.top := null; - self.size := 0 - }; - - /// Creates a deep copy of the stack with the same elements in the same order. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let original = Stack.fromIter([3, 2, 1].values()); - /// let copy = Stack.clone(original); - /// assert Stack.equal(copy, original, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack. - public func clone(self : Stack) : Stack { - let copy = empty(); - for (element in values(self)) { - push(copy, element) - }; - reverse(copy); - copy - }; - - /// Returns true if the stack contains no elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// assert Stack.isEmpty(stack); - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func isEmpty(self : Stack) : Bool { - self.size == 0 - }; - - /// Returns the number of elements on the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.size(stack) == 3; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func size(self : Stack) : Nat { - self.size - }; - - /// Returns true if the stack contains the specified element. - /// Uses the provided equality function to compare elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.contains(stack, Nat.equal, 2); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and assuming - /// that `equal` has O(1) costs. - public func contains(self : Stack, equal : (implicit : (T, T) -> Bool), element : T) : Bool { - for (existing in values(self)) { - if (equal(existing, element)) { - return true - } - }; - false - }; - - public func reverseValues(self : Stack) : Iter.Iter { - Iter.reverse(values(self)) - }; - - /// Pushes a new element onto the top of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 42); - /// assert Stack.peek(stack) == ?42; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func push(self : Stack, value : T) { - self.top := ?(value, self.top); - self.size += 1 - }; - - /// Returns the top element of the stack without removing it. - /// Returns null if the stack is empty. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// assert Stack.peek(stack) == ?1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func peek(self : Stack) : ?T { - switch (self.top) { - case null null; - case (?(value, _)) ?value - } - }; - - /// Removes and returns the top element of the stack. - /// Returns null if the stack is empty. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// assert Stack.pop(stack) == ?1; - /// assert Stack.pop(stack) == ?2; - /// assert Stack.pop(stack) == ?3; - /// assert Stack.pop(stack) == null; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func pop(self : Stack) : ?T { - switch (self.top) { - case null null; - case (?(value, next)) { - self.top := next; - self.size -= 1; - ?value - } - } - }; - - /// Returns the element at the specified position from the top of the stack. - /// Returns null if position is out of bounds. - /// Position 0 is the top of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 'c'); - /// Stack.push(stack, 'b'); - /// Stack.push(stack, 'a'); - /// assert Stack.get(stack, 0) == ?'a'; - /// assert Stack.get(stack, 1) == ?'b'; - /// assert Stack.get(stack, 2) == ?'c'; - /// assert Stack.get(stack, 3) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack. - public func get(self : Stack, position : Nat) : ?T { - var index = 0; - var current = self.top; - while (index < position) { - switch (current) { - case null return null; - case (?(_, next)) { - current := next - } - }; - index += 1 - }; - switch (current) { - case null null; - case (?(value, _)) ?value - } - }; - - /// Reverses the order of elements in the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// Stack.reverse(stack); - /// assert Stack.pop(stack) == ?3; - /// assert Stack.pop(stack) == ?2; - /// assert Stack.pop(stack) == ?1; - /// assert Stack.pop(stack) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack. - public func reverse(self : Stack) { - var last : List = null; - for (element in values(self)) { - last := ?(element, last) - }; - self.top := last - }; - - /// Returns an iterator over the elements in the stack, from top to bottom. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// assert Iter.toArray(Stack.values(stack)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: O(1) for iterator creation, O(n) for full traversal - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack. - public func values(self : Stack) : Types.Iter { - object { - var current = self.top; - - public func next() : ?T { - switch (current) { - case null null; - case (?(value, next)) { - current := next; - ?value - } - } - } - } - }; - - /// Returns true if all elements in the stack satisfy the predicate. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([2, 4, 6].values()); - /// assert Stack.all(stack, func(n) = n % 2 == 0); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `predicate` has O(1) costs. - public func all(self : Stack, predicate : T -> Bool) : Bool { - for (element in values(self)) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Returns true if any element in the stack satisfies the predicate. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.any(stack, func(n) = n == 2); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming `predicate` has O(1) costs. - public func any(self : Stack, predicate : T -> Bool) : Bool { - for (element in values(self)) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Applies the operation to each element in the stack, from top to bottom. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// var text = ""; - /// Stack.forEach(stack, func(n) = text #= Nat.toText(n)); - /// assert text == "123"; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `operation` has O(1) costs. - public func forEach(self : Stack, operation : T -> ()) { - for (element in values(self)) { - operation(element) - } - }; - - /// Creates a new stack by applying the projection function to each element. - /// Maintains the original order of elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// let doubled = Stack.map(stack, func(n) { 2 * n }); - /// assert Stack.get(doubled, 0) == ?2; - /// assert Stack.get(doubled, 1) == ?4; - /// assert Stack.get(doubled, 2) == ?6; - /// assert Stack.get(doubled, 3) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `project` has O(1) costs. - public func map(self : Stack, project : T -> U) : Stack { - let result = empty(); - for (element in values(self)) { - push(result, project(element)) - }; - reverse(result); - result - }; - - /// Creates a new stack containing only elements that satisfy the predicate. - /// Maintains the relative order of elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 4); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// let evens = Stack.filter(stack, func(n) { n % 2 == 0 }); - /// assert Stack.pop(evens) == ?2; - /// assert Stack.pop(evens) == ?4; - /// assert Stack.pop(evens) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming `predicate` has O(1) costs. - public func filter(self : Stack, predicate : T -> Bool) : Stack { - let result = empty(); - for (element in values(self)) { - if (predicate(element)) { - push(result, element) - } - }; - reverse(result); - result - }; - - /// Creates a new stack by applying the projection function to each element - /// and keeping only the successful results (where project returns ?value). - /// Maintains the relative order of elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 4); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// let evenDoubled = Stack.filterMap(stack, func(n) { - /// if (n % 2 == 0) { - /// ?(n * 2) - /// } else { - /// null - /// } - /// }); - /// assert Stack.pop(evenDoubled) == ?4; - /// assert Stack.pop(evenDoubled) == ?8; - /// assert Stack.pop(evenDoubled) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `project` has O(1) costs. - public func filterMap(self : Stack, project : T -> ?U) : Stack { - let result = empty(); - for (element in values(self)) { - switch (project(element)) { - case null {}; - case (?newElement) { - push(result, newElement) - } - } - }; - reverse(result); - result - }; - - /// Return the first element for which the given `predicate` is true, - /// if such an element exists. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromPure(?(1, ?(2, ?(3, null)))); - /// assert Stack.find(stack, func n = n > 1) == ?2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - - public func find(self : Stack, predicate : T -> Bool) : ?T = PureList.find(self.top, predicate); - - /// Return the first index for which the given `predicate` is true. - /// If no element satisfies the predicate, returns null. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromPure(?('A', ?('B', ?('C', ?('D', null))))); - /// let found = Stack.findIndex(stack, func x = x == 'C'); - /// assert found == ?2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func findIndex(self : Stack, predicate : T -> Bool) : ?Nat = PureList.findIndex(self.top, predicate); - - /// Compares two stacks for equality using the provided equality function. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack1 = Stack.fromIter([3, 2, 1].values()); - /// let stack2 = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.equal(stack1, stack2, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `equal` has O(1) costs. - public func equal(self : Stack, other : Stack, equal : (implicit : (T, T) -> Bool)) : Bool { - if (size(self) != size(other)) { - return false - }; - let iterator1 = values(self); - let iterator2 = values(other); - loop { - let element1 = iterator1.next(); - let element2 = iterator2.next(); - switch (element1, element2) { - case (null, null) { - return true - }; - case (?element1, ?element2) { - if (not equal(element1, element2)) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Creates a new stack from an iterator. - /// Elements are pushed in iteration order. Which means that the last element - /// of the iterator will be the first element on top of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Iter.toArray(Stack.values(stack)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of iterated elements. - public func fromIter(iter : Types.Iter) : Stack { - let stack = empty(); - for (element in iter) { - push(stack, element) - }; - stack - }; - - /// Convert an iterator into a stack. - /// Elements are pushed in iteration order. Which means that the last element - /// of the iterator will be the first element on top of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = [3, 2, 1].values(); - /// - /// let stack = iter.toStack(); - /// - /// assert Iter.toArray(Stack.values(stack)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of iterated elements. - public func toStack(self : Types.Iter) : Stack { - fromIter(self) - }; - - /// Converts the stack to its string representation using the provided - /// element formatting function. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.toText(stack, Nat.toText) == "Stack[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `format` has O(1) costs. - public func toText(self : Stack, format : (implicit : (toText : T -> Text))) : Text { - var text = "Stack["; - var sep = ""; - for (element in values(self)) { - text #= sep # format(element); - sep := ", " - }; - text #= "]"; - text - }; - - /// Compares two stacks lexicographically using the provided comparison function. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack1 = Stack.fromIter([2, 1].values()); - /// let stack2 = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.compare(stack1, stack2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `compare` has O(1) costs. - public func compare(self : Stack, other : Stack, compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let iterator1 = values(self); - let iterator2 = values(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?element1, ?element2) { - let comparison = compare(element1, element2); - if (comparison != #equal) { - return comparison - } - } - } - } - } -} diff --git a/.mops/core@2.3.1/src/Text.mo b/.mops/core@2.3.1/src/Text.mo deleted file mode 100644 index 1f6c8a5..0000000 --- a/.mops/core@2.3.1/src/Text.mo +++ /dev/null @@ -1,967 +0,0 @@ -/// Utility functions for `Text` values. -/// -/// A `Text` value represents human-readable text as a sequence of characters of type `Char`. -/// -/// ```motoko -/// let text = "Hello!"; -/// let size = text.size(); -/// assert size == 6; -/// let iter = text.chars(); -/// assert iter.next() == ?'H'; -/// assert iter.next() == ?'e'; -/// assert iter.next() == ?'l'; -/// assert iter.next() == ?'l'; -/// assert iter.next() == ?'o'; -/// assert iter.next() == ?'!'; -/// assert iter.next() == null; -/// let concat = text # " 👋"; -/// assert concat == "Hello! 👋"; -/// ``` -/// -/// The `"mo:core/Text"` module defines additional operations on `Text` values. -/// -/// Import the module from the core package: -/// -/// ```motoko name=import -/// import Text "mo:core/Text"; -/// ``` -/// -/// Note: `Text` values are represented as ropes of UTF-8 character sequences with O(1) concatenation. -/// - -import Char "Char"; -import Iter "Iter"; -import Stack "Stack"; -import Types "Types"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// The type corresponding to primitive `Text` values. - /// - /// ```motoko - /// let hello = "Hello!"; - /// let emoji = "👋"; - /// let concat = hello # " " # emoji; - /// assert concat == "Hello! 👋"; - /// ``` - public type Text = Prim.Types.Text; - - /// Converts the given `Char` to a `Text` value. - /// - /// ```motoko include=import - /// let text = Text.fromChar('A'); - /// assert text == "A"; - /// ``` - public let fromChar : (c : Char) -> Text = Prim.charToText; - - /// Converts the given `[Char]` to a `Text` value. - /// - /// ```motoko include=import - /// let text = Text.fromArray(['A', 'v', 'o', 'c', 'a', 'd', 'o']); - /// assert text == "Avocado"; - /// ``` - /// - /// Runtime: O(a.size()) - /// Space: O(a.size()) - public func fromArray(a : [Char]) : Text = fromIter(a.vals()); - - /// Converts the given `[var Char]` to a `Text` value. - /// - /// ```motoko include=import - /// let text = Text.fromVarArray([var 'E', 'g', 'g', 'p', 'l', 'a', 'n', 't']); - /// assert text == "Eggplant"; - /// ``` - /// - /// Runtime: O(a.size()) - /// Space: O(a.size()) - public func fromVarArray(a : [var Char]) : Text = fromIter(a.vals()); - - /// Iterates over each `Char` value in the given `Text`. - /// - /// Equivalent to calling the `t.chars()` method where `t` is a `Text` value. - /// - /// ```motoko include=import - /// let chars = Text.toIter("abc"); - /// assert chars.next() == ?'a'; - /// assert chars.next() == ?'b'; - /// assert chars.next() == ?'c'; - /// assert chars.next() == null; - /// ``` - public func toIter(self : Text) : Iter.Iter = self.chars(); - - /// Collapses the characters in `text` into a single value by starting with `base` - /// and progessively combining characters into `base` with `combine`. Iteration runs - /// left to right. - /// - /// ```motoko include=import - /// - /// let text = "Mississippi"; - /// let count = - /// Text.foldLeft( - /// text, - /// 0, // start the sum at 0 - /// func(ss, c) = if (c == 's') ss + 1 else ss - /// ); - /// assert count == 4; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(self : Text, base : A, combine : (A, Char) -> A) : A { - var acc = base; - for (c in self.chars()) acc := combine(acc, c); - acc - }; - - /// Creates a new `Array` containing characters of the given `Text`. - /// - /// Equivalent to `Iter.toArray(t.chars())`. - /// - /// ```motoko include=import - /// assert Text.toArray("Café") == ['C', 'a', 'f', 'é']; - /// ``` - /// - /// Runtime: O(t.size()) - /// Space: O(t.size()) - public func toArray(self : Text) : [Char] { - let cs = self.chars(); - // We rely on Array_tabulate's implementation details: it fills - // the array from left to right sequentially. - Prim.Array_tabulate( - self.size(), - func _ { - switch (cs.next()) { - case (?c) { c }; - case null { Prim.trap("Text.toArray()") } - } - } - ) - }; - - /// Creates a new mutable `Array` containing characters of the given `Text`. - /// - /// Equivalent to `Iter.toArrayMut(t.chars())`. - /// - /// ```motoko include=import - /// import VarArray "mo:core/VarArray"; - /// import Char "mo:core/Char"; - /// - /// assert VarArray.equal(Text.toVarArray("Café"), [var 'C', 'a', 'f', 'é'], Char.equal); - /// ``` - /// - /// Runtime: O(t.size()) - /// Space: O(t.size()) - public func toVarArray(self : Text) : [var Char] { - let n = self.size(); - if (n == 0) { - return [var] - }; - let array = Prim.Array_init(n, ' '); - var i = 0; - for (c in self.chars()) { - array[i] := c; - i += 1 - }; - array - }; - - /// Creates a `Text` value from a `Char` iterator. - /// - /// ```motoko include=import - /// let text = Text.fromIter(['a', 'b', 'c'].values()); - /// assert text == "abc"; - /// ``` - public func fromIter(cs : Iter.Iter) : Text { - var r = ""; - for (c in cs) { - r #= Prim.charToText(c) - }; - return r - }; - - /// Returns whether the given `Text` is empty (has a size of zero). - /// - /// ```motoko include=import - /// let text1 = ""; - /// let text2 = "example"; - /// assert Text.isEmpty(text1); - /// assert not Text.isEmpty(text2); - /// ``` - public func isEmpty(self : Text) : Bool = self == ""; - - /// Returns the number of characters in the given `Text`. - /// - /// Equivalent to calling `t.size()` where `t` is a `Text` value. - /// - /// ```motoko include=import - /// let size = Text.size("abc"); - /// assert size == 3; - /// ``` - public func size(self : Text) : Nat = self.size(); - - /// Returns `t1 # t2`, where `#` is the `Text` concatenation operator. - /// - /// ```motoko include=import - /// let a = "Hello"; - /// let b = "There"; - /// let together = a # b; - /// assert together == "HelloThere"; - /// let withSpace = a # " " # b; - /// assert withSpace == "Hello There"; - /// let togetherAgain = Text.concat(a, b); - /// assert togetherAgain == "HelloThere"; - /// ``` - public func concat(self : Text, other : Text) : Text = self # other; - - /// Returns a new `Text` with the characters of the input `Text` in reverse order. - /// - /// ```motoko include=import - /// let text = Text.reverse("Hello"); - /// assert text == "olleH"; - /// ``` - /// - /// Runtime: O(t.size()) - /// Space: O(t.size()) - public func reverse(self : Text) : Text { - fromIter(Iter.reverse(self.chars())) - }; - - /// Returns true if two text values are equal. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.equal("hello", "hello"); - /// assert not Text.equal("hello", "world"); - /// ``` - public func equal(self : Text, other : Text) : Bool { self == other }; - - /// Returns true if two text values are not equal. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.notEqual("hello", "world"); - /// assert not Text.notEqual("hello", "hello"); - /// ``` - public func notEqual(self : Text, other : Text) : Bool { self != other }; - - /// Returns true if the first text value is lexicographically less than the second. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.less("apple", "banana"); - /// assert not Text.less("banana", "apple"); - /// ``` - public func less(self : Text, other : Text) : Bool { self < other }; - - /// Returns true if the first text value is lexicographically less than or equal to the second. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.lessOrEqual("apple", "banana"); - /// assert Text.lessOrEqual("apple", "apple"); - /// assert not Text.lessOrEqual("banana", "apple"); - /// ``` - public func lessOrEqual(self : Text, other : Text) : Bool { self <= other }; - - /// Returns true if the first text value is lexicographically greater than the second. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.greater("banana", "apple"); - /// assert not Text.greater("apple", "banana"); - /// ``` - public func greater(self : Text, other : Text) : Bool { self > other }; - - /// Returns true if the first text value is lexicographically greater than or equal to the second. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.greaterOrEqual("banana", "apple"); - /// assert Text.greaterOrEqual("apple", "apple"); - /// assert not Text.greaterOrEqual("apple", "banana"); - /// ``` - public func greaterOrEqual(self : Text, other : Text) : Bool { self >= other }; - - /// Compares `t1` and `t2` lexicographically. - /// - /// ```motoko include=import - /// assert Text.compare("abc", "abc") == #equal; - /// assert Text.compare("abc", "def") == #less; - /// assert Text.compare("abc", "ABC") == #greater; - /// ``` - public func compare(self : Text, other : Text) : Order.Order { - let c = Prim.textCompare(self, other); - if (c < 0) #less else if (c == 0) #equal else #greater - }; - - private func extract(self : Text, i : Nat, j : Nat) : Text { - let size = self.size(); - if (i == 0 and j == size) return self; - assert (j <= size); - let cs = self.chars(); - var r = ""; - var n = i; - while (n > 0) { - ignore cs.next(); - n -= 1 - }; - n := j; - while (n > 0) { - switch (cs.next()) { - case null { assert false }; - case (?c) { r #= Prim.charToText(c) } - }; - n -= 1 - }; - return r - }; - - /// Join an iterator of `Text` values with a given delimiter. - /// - /// ```motoko include=import - /// let joined = Text.join(["a", "b", "c"].values(), ", "); - /// assert joined == "a, b, c"; - /// ``` - public func join(self : Iter.Iter, sep : Text) : Text { - var r = ""; - if (sep.size() == 0) { - for (t in self) { - r #= t - }; - return r - }; - let next = self.next; - switch (next()) { - case null { return r }; - case (?t) { - r #= t - } - }; - loop { - switch (next()) { - case null { return r }; - case (?t) { - r #= sep; - r #= t - } - } - } - }; - - /// Applies a function to each character in a `Text` value, returning the concatenated `Char` results. - /// - /// ```motoko include=import - /// // Replace all occurrences of '?' with '!' - /// let result = Text.map("Motoko?", func(c) { - /// if (c == '?') '!' - /// else c - /// }); - /// assert result == "Motoko!"; - /// ``` - public func map(self : Text, f : Char -> Char) : Text { - var r = ""; - for (c in self.chars()) { - r #= Prim.charToText(f(c)) - }; - r - }; - - /// Returns the result of applying `f` to each character in `ts`, concatenating the intermediate text values. - /// - /// ```motoko include=import - /// // Replace all occurrences of '?' with "!!" - /// let result = Text.flatMap("Motoko?", func(c) { - /// if (c == '?') "!!" - /// else Text.fromChar(c) - /// }); - /// assert result == "Motoko!!"; - /// ``` - public func flatMap(self : Text, f : Char -> Text) : Text { - var r = ""; - for (c in self.chars()) { - r #= f(c) - }; - r - }; - - /// A pattern `p` describes a sequence of characters. A pattern has one of the following forms: - /// - /// * `#char c` matches the single character sequence, `c`. - /// * `#text t` matches multi-character text sequence `t`. - /// * `#predicate p` matches any single character sequence `c` satisfying predicate `p(c)`. - /// - /// A _match_ for `p` is any sequence of characters matching the pattern `p`. - /// - /// ```motoko include=import - /// let charPattern = #char 'A'; - /// let textPattern = #text "phrase"; - /// let predicatePattern : Text.Pattern = #predicate (func(c) { c == 'A' or c == 'B' }); - /// assert Text.contains("A", predicatePattern); - /// assert Text.contains("B", predicatePattern); - /// ``` - public type Pattern = Types.Pattern; - - private func take(n : Nat, cs : Iter.Iter) : Iter.Iter { - var i = n; - object { - public func next() : ?Char { - if (i == 0) return null; - i -= 1; - return cs.next() - } - } - }; - - private func empty() : Iter.Iter { - object { - public func next() : ?Char = null - } - }; - - private type Match = { - /// #success on complete match - #success; - /// #fail(cs,c) on partial match of cs, but failing match on c - #fail : (cs : Iter.Iter, c : Char); - /// #empty(cs) on partial match of cs and empty stream - #empty : (cs : Iter.Iter) - }; - - private func sizeOfPattern(pat : Pattern) : Nat { - switch pat { - case (#text(t)) { t.size() }; - case (#predicate(_) or #char(_)) { 1 } - } - }; - - private func matchOfPattern(pat : Pattern) : (cs : Iter.Iter) -> Match { - switch pat { - case (#char(p)) { - func(cs : Iter.Iter) : Match { - switch (cs.next()) { - case (?c) { - if (p == c) { - #success - } else { - #fail(empty(), c) - } - }; - case null { #empty(empty()) } - } - } - }; - case (#predicate(p)) { - func(cs : Iter.Iter) : Match { - switch (cs.next()) { - case (?c) { - if (p(c)) { - #success - } else { - #fail(empty(), c) - } - }; - case null { #empty(empty()) } - } - } - }; - case (#text(p)) { - func(cs : Iter.Iter) : Match { - var i = 0; - let ds = p.chars(); - loop { - switch (ds.next()) { - case (?d) { - switch (cs.next()) { - case (?c) { - if (c != d) { - return #fail(take(i, p.chars()), c) - }; - i += 1 - }; - case null { - return #empty(take(i, p.chars())) - } - } - }; - case null { return #success } - } - } - } - } - } - }; - - private class CharBuffer(cs : Iter.Iter) : Iter.Iter = { - - var stack : Stack.Stack<(Iter.Iter, Char)> = Stack.empty(); - - public func pushBack(cs0 : Iter.Iter, c : Char) { - Stack.push(stack, (cs0, c)) - }; - - public func next() : ?Char { - switch (Stack.peek(stack)) { - case (?(buff, c)) { - switch (buff.next()) { - case null { - ignore Stack.pop(stack); - return ?c - }; - case oc { - return oc - } - } - }; - case null { - return cs.next() - } - } - } - }; - - /// Splits the input `Text` with the specified `Pattern`. - /// - /// Two fields are separated by exactly one match. - /// - /// ```motoko include=import - /// let words = Text.split("This is a sentence.", #char ' '); - /// assert Text.join(words, "|") == "This|is|a|sentence."; - /// ``` - public func split(self : Text, p : Pattern) : Iter.Iter { - let match = matchOfPattern(p); - let cs = CharBuffer(self.chars()); - var state = 0; - var field = ""; - object { - public func next() : ?Text { - switch state { - case (0 or 1) { - loop { - switch (match(cs)) { - case (#success) { - let r = field; - field := ""; - state := 1; - return ?r - }; - case (#empty(cs1)) { - for (c in cs1) { - field #= fromChar(c) - }; - let r = if (state == 0 and field == "") { - null - } else { - ?field - }; - state := 2; - return r - }; - case (#fail(cs1, c)) { - cs.pushBack(cs1, c); - switch (cs.next()) { - case (?ci) { - field #= fromChar(ci) - }; - case null { - let r = if (state == 0 and field == "") { - null - } else { - ?field - }; - state := 2; - return r - } - } - } - } - } - }; - case _ { return null } - } - } - } - }; - - /// Returns a sequence of tokens from the input `Text` delimited by the specified `Pattern`, derived from start to end. - /// A "token" is a non-empty maximal subsequence of `t` not containing a match for pattern `p`. - /// Two tokens may be separated by one or more matches of `p`. - /// - /// ```motoko include=import - /// let tokens = Text.tokens("this needs\n an example", #predicate (func(c) { c == ' ' or c == '\n' })); - /// assert Text.join(tokens, "|") == "this|needs|an|example"; - /// ``` - public func tokens(self : Text, p : Pattern) : Iter.Iter { - let fs = split(self, p); - object { - public func next() : ?Text { - switch (fs.next()) { - case (?"") { next() }; - case ot { ot } - } - } - } - }; - - /// Returns `true` if the input `Text` contains a match for the specified `Pattern`. - /// - /// ```motoko include=import - /// assert Text.contains("Motoko", #text "oto"); - /// assert not Text.contains("Motoko", #text "xyz"); - /// ``` - public func contains(self : Text, p : Pattern) : Bool { - let match = matchOfPattern(p); - let cs = CharBuffer(self.chars()); - loop { - switch (match(cs)) { - case (#success) { - return true - }; - case (#empty(_cs1)) { - return false - }; - case (#fail(cs1, c)) { - cs.pushBack(cs1, c); - switch (cs.next()) { - case null { - return false - }; - case _ {}; // continue - } - } - } - } - }; - - /// Returns `true` if the input `Text` starts with a prefix matching the specified `Pattern`. - /// - /// ```motoko include=import - /// assert Text.startsWith("Motoko", #text "Mo"); - /// ``` - public func startsWith(self : Text, p : Pattern) : Bool { - var cs = self.chars(); - let match = matchOfPattern(p); - switch (match(cs)) { - case (#success) { true }; - case _ { false } - } - }; - - /// Returns `true` if the input `Text` ends with a suffix matching the specified `Pattern`. - /// - /// ```motoko include=import - /// assert Text.endsWith("Motoko", #char 'o'); - /// ``` - public func endsWith(self : Text, p : Pattern) : Bool { - let s2 = sizeOfPattern(p); - if (s2 == 0) return true; - let s1 = self.size(); - if (s2 > s1) return false; - let match = matchOfPattern(p); - var cs1 = self.chars(); - var diff : Nat = s1 - s2; - while (diff > 0) { - ignore cs1.next(); - diff -= 1 - }; - switch (match(cs1)) { - case (#success) { true }; - case _ { false } - } - }; - - /// Returns the input text `t` with all matches of pattern `p` replaced by text `r`. - /// - /// ```motoko include=import - /// let result = Text.replace("abcabc", #char 'a', "A"); - /// assert result == "AbcAbc"; - /// ``` - public func replace(self : Text, p : Pattern, r : Text) : Text { - let match = matchOfPattern(p); - let size = sizeOfPattern(p); - let cs = CharBuffer(self.chars()); - var res = ""; - label l loop { - switch (match(cs)) { - case (#success) { - res #= r; - if (size > 0) { - continue l - } - }; - case (#empty(cs1)) { - for (c1 in cs1) { - res #= fromChar(c1) - }; - break l - }; - case (#fail(cs1, c)) { - cs.pushBack(cs1, c) - } - }; - switch (cs.next()) { - case null { - break l - }; - case (?c1) { - res #= fromChar(c1) - }; // continue - } - }; - return res - }; - - /// Strips one occurrence of the given `Pattern` from the beginning of the input `Text`. - /// If you want to remove multiple instances of the pattern, use `Text.trimStart()` instead. - /// - /// ```motoko include=import - /// // Try to strip a nonexistent character - /// let none = Text.stripStart("abc", #char '-'); - /// assert none == null; - /// // Strip just one '-' - /// let one = Text.stripStart("--abc", #char '-'); - /// assert one == ?"-abc"; - /// ``` - public func stripStart(self : Text, p : Pattern) : ?Text { - let s = sizeOfPattern(p); - if (s == 0) return ?self; - var cs = self.chars(); - let match = matchOfPattern(p); - switch (match(cs)) { - case (#success) return ?fromIter(cs); - case _ return null - } - }; - - /// Strips one occurrence of the given `Pattern` from the end of the input `Text`. - /// If you want to remove multiple instances of the pattern, use `Text.trimEnd()` instead. - /// - /// ```motoko include=import - /// // Try to strip a nonexistent character - /// let none = Text.stripEnd("xyz", #char '-'); - /// assert none == null; - /// // Strip just one '-' - /// let one = Text.stripEnd("xyz--", #char '-'); - /// assert one == ?"xyz-"; - /// ``` - public func stripEnd(self : Text, p : Pattern) : ?Text { - let s2 = sizeOfPattern(p); - if (s2 == 0) return ?self; - let s1 = self.size(); - if (s2 > s1) return null; - let match = matchOfPattern(p); - var cs1 = self.chars(); - var diff : Nat = s1 - s2; - while (diff > 0) { - ignore cs1.next(); - diff -= 1 - }; - switch (match(cs1)) { - case (#success) return ?extract(self, 0, s1 - s2); - case _ return null - } - }; - - /// Trims the given `Pattern` from the start of the input `Text`. - /// If you only want to remove a single instance of the pattern, use `Text.stripStart()` instead. - /// - /// ```motoko include=import - /// let trimmed = Text.trimStart("---abc", #char '-'); - /// assert trimmed == "abc"; - /// ``` - public func trimStart(self : Text, p : Pattern) : Text { - let cs = self.chars(); - let size = sizeOfPattern(p); - if (size == 0) return self; - var matchSize = 0; - let match = matchOfPattern(p); - loop { - switch (match(cs)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(cs1)) { - return if (matchSize == 0) { - self - } else { - fromIter(cs1) - } - }; - case (#fail(cs1, c)) { - return if (matchSize == 0) { - self - } else { - fromIter(cs1) # fromChar(c) # fromIter(cs) - } - } - } - } - }; - - /// Trims the given `Pattern` from the end of the input `Text`. - /// If you only want to remove a single instance of the pattern, use `Text.stripEnd()` instead. - /// - /// ```motoko include=import - /// let trimmed = Text.trimEnd("xyz---", #char '-'); - /// assert trimmed == "xyz"; - /// ``` - public func trimEnd(self : Text, p : Pattern) : Text { - let cs = CharBuffer(self.chars()); - let size = sizeOfPattern(p); - if (size == 0) return self; - let match = matchOfPattern(p); - var matchSize = 0; - label l loop { - switch (match(cs)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(cs1)) { - switch (cs1.next()) { - case null break l; - case (?_) return self - } - }; - case (#fail(cs1, c)) { - matchSize := 0; - cs.pushBack(cs1, c); - ignore cs.next() - } - } - }; - extract(self, 0, self.size() - matchSize) - }; - - /// Trims the given `Pattern` from both the start and end of the input `Text`. - /// - /// ```motoko include=import - /// let trimmed = Text.trim("---abcxyz---", #char '-'); - /// assert trimmed == "abcxyz"; - /// ``` - public func trim(self : Text, p : Pattern) : Text { - let cs = self.chars(); - let size = sizeOfPattern(p); - if (size == 0) return self; - var matchSize = 0; - let match = matchOfPattern(p); - loop { - switch (match(cs)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(cs1)) { - return if (matchSize == 0) { self } else { fromIter(cs1) } - }; - case (#fail(cs1, c)) { - let start = matchSize; - let cs2 = CharBuffer(cs); - cs2.pushBack(cs1, c); - ignore cs2.next(); - matchSize := 0; - label l loop { - switch (match(cs2)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(_cs3)) { - switch (cs1.next()) { - case null break l; - case (?_) return self - } - }; - case (#fail(cs3, c1)) { - matchSize := 0; - cs2.pushBack(cs3, c1); - ignore cs2.next() - } - } - }; - return extract(self, start, self.size() - matchSize - start) - } - } - } - }; - - /// Compares `t1` and `t2` using the provided character-wise comparison function. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// assert Text.compareWith("abc", "ABC", func(c1, c2) { Char.compare(c1, c2) }) == #greater; - /// ``` - public func compareWith( - self : Text, - other : Text, - compare : (Char, Char) -> Order.Order - ) : Order.Order { - let cs1 = self.chars(); - let cs2 = other.chars(); - loop { - switch (cs1.next(), cs2.next()) { - case (null, null) { return #equal }; - case (null, ?_) { return #less }; - case (?_, null) { return #greater }; - case (?c1, ?c2) { - switch (compare(c1, c2)) { - case (#equal) {}; // continue - case other { return other } - } - } - } - } - }; - - /// Returns a UTF-8 encoded `Blob` from the given `Text`. - /// - /// ```motoko include=import - /// let blob = Text.encodeUtf8("Hello"); - /// assert blob == "\48\65\6C\6C\6F"; - /// ``` - public let encodeUtf8 : (self : Text) -> Blob = Prim.encodeUtf8; - - /// Tries to decode the given `Blob` as UTF-8. - /// Returns `null` if the blob is not valid UTF-8. - /// - /// ```motoko include=import - /// let text = Text.decodeUtf8("\48\65\6C\6C\6F"); - /// assert text == ?"Hello"; - /// ``` - public let decodeUtf8 : (self : Blob) -> ?Text = Prim.decodeUtf8; - - /// Returns the text argument in lowercase. - /// WARNING: Unicode compliant only when compiled, not interpreted. - /// - /// ```motoko include=import - /// let text = Text.toLower("Good Day"); - /// assert text == "good day"; - /// ``` - public let toLower : (self : Text) -> Text = Prim.textLowercase; - - /// Returns the text argument in uppercase. Unicode compliant. - /// WARNING: Unicode compliant only when compiled, not interpreted. - /// - /// ```motoko include=import - /// let text = Text.toUpper("Good Day"); - /// assert text == "GOOD DAY"; - /// ``` - public let toUpper : (self : Text) -> Text = Prim.textUppercase; - - /// Returns the given text value unchanged. - /// This function is provided for consistency with other modules. - /// - /// ```motoko include=import - /// assert Text.toText("Hello") == "Hello"; - /// ``` - public func toText(self : Text) : Text = self - -} diff --git a/.mops/core@2.3.1/src/Time.mo b/.mops/core@2.3.1/src/Time.mo deleted file mode 100644 index 00197a7..0000000 --- a/.mops/core@2.3.1/src/Time.mo +++ /dev/null @@ -1,62 +0,0 @@ -/// System time utilities and timers. -/// -/// The following example illustrates using the system time: -/// -/// ```motoko -/// import Int = "mo:core/Int"; -/// import Time = "mo:core/Time"; -/// -/// persistent actor { -/// var lastTime = Time.now(); -/// -/// public func greet(name : Text) : async Text { -/// let now = Time.now(); -/// let elapsedSeconds = (now - lastTime) / 1000_000_000; -/// lastTime := now; -/// return "Hello, " # name # "!" # -/// " I was last called " # Int.toText(elapsedSeconds) # " seconds ago"; -/// }; -/// }; -/// ``` -/// -/// Note: If `moc` is invoked with `-no-timer`, the importing will fail. -/// Note: The resolution of the timers is in the order of the block rate, -/// so durations should be chosen well above that. For frequent -/// canister wake-ups the heartbeat mechanism should be considered. - -import Types "Types"; -import Nat "Nat"; -import Prim "mo:⛔"; - -module { - - /// System time is represent as nanoseconds since 1970-01-01. - public type Time = Types.Time; - - /// Quantity of time expressed in `#days`, `#hours`, `#minutes`, `#seconds`, `#milliseconds`, or `#nanoseconds`. - public type Duration = Types.Duration; - - /// Current system time given as nanoseconds since 1970-01-01. The system guarantees that: - /// - /// * the time, as observed by the canister smart contract, is monotonically increasing, even across canister upgrades. - /// * within an invocation of one entry point, the time is constant. - /// - /// The system times of different canisters are unrelated, and calls from one canister to another may appear to travel "backwards in time" - /// - /// Note: While an implementation will likely try to keep the system time close to the real time, this is not formally guaranteed. - public func now() : Time = Prim.nat64ToNat(Prim.time()); - - public type TimerId = Nat; - - public func toNanoseconds(duration : Duration) : Nat { - switch duration { - case (#days n) n * 86_400_000_000_000; - case (#hours n) n * 3_600_000_000_000; - case (#minutes n) n * 60_000_000_000; - case (#seconds n) n * 1_000_000_000; - case (#milliseconds n) n * 1_000_000; - case (#nanoseconds n) n - } - }; - -} diff --git a/.mops/core@2.3.1/src/Timer.mo b/.mops/core@2.3.1/src/Timer.mo deleted file mode 100644 index 6f4f377..0000000 --- a/.mops/core@2.3.1/src/Timer.mo +++ /dev/null @@ -1,84 +0,0 @@ -/// Timers for one-off or periodic tasks. Applicable as part of the default mechanism. -/// If `moc` is invoked with `-no-timer`, the importing will fail. Furthermore, if passed `--trap-on-call-error`, a congested canister send queue may prevent timer expirations to execute at runtime. It may also deactivate the global timer. -/// -/// ```motoko name=import -/// import Timer "mo:core/Timer"; -/// ``` -/// -/// The resolution of the timers is similar to the block rate, -/// so durations should be chosen well above that. For frequent -/// canister wake-ups, consider using the [heartbeat](https://internetcomputer.org/docs/motoko/icp-features/system-functions#heartbeat) mechanism; however, when possible, canisters should prefer timers. -/// -/// The functionality described below is enabled only when the actor does not override it by declaring an explicit `system func timer`. -/// -/// Timers are _not_ persisted across upgrades. One possible strategy -/// to re-establish timers after an upgrade is to use stable variables -/// in the `post_upgrade` hook and distill necessary timer information -/// from there. -/// -/// Using timers for security (e.g., access control) is strongly discouraged. -/// Make sure to inform yourself about state-of-the-art dapp security. -/// If you must use timers for security controls, be sure -/// to consider reentrancy issues as well as the vanishing of timers on upgrades -/// and reinstalls. -/// -/// For further usage information for timers on the IC, please consult -/// [the documentation](https://internetcomputer.org/docs/building-apps/network-features/periodic-tasks-timers#timers-library-limitations). -import { setTimer = setTimerNano; cancelTimer = cancel } = "mo:⛔"; -import Nat64 = "Nat64"; -import Time "Time"; - -module { - - public type TimerId = Nat; - - /// Installs a one-off timer that upon expiration after given duration `d` - /// executes the future `job()`. - /// - /// ```motoko include=import no-repl - /// import Int "mo:core/Int"; - /// - /// func runIn30Minutes() : async () { - /// // ... - /// }; - /// let timerId = Timer.setTimer(#minutes 30, runIn30Minutes); - /// ``` - public func setTimer(duration : Time.Duration, job : () -> async ()) : TimerId { - setTimerNano(Nat64.fromNat(Time.toNanoseconds duration), false, job) - }; - - /// Installs a recurring timer that upon expiration after given duration `d` - /// executes the future `job()` and reinserts itself for another expiration. - /// - /// Note: A duration of 0 will only expire once. - /// - /// ```motoko include=import no-repl - /// func runEvery30Minutes() : async () { - /// // ... - /// }; - /// let timerId = Timer.recurringTimer(#minutes 30, runEvery30Minutes); - /// ``` - public func recurringTimer(duration : Time.Duration, job : () -> async ()) : TimerId { - setTimerNano(Nat64.fromNat(Time.toNanoseconds duration), true, job) - }; - - /// Cancels a still active timer with `(id : TimerId)`. For expired timers - /// and not recognised `id`s nothing happens. - /// - /// ```motoko include=import no-repl - /// var counter = 0; - /// var timerId : ?Timer.TimerId = null; - /// func runFiveTimes() : async () { - /// counter += 1; - /// if (counter == 5) { - /// switch (timerId) { - /// case (?id) { Timer.cancelTimer(id) }; - /// case null { assert false /* timer already cancelled */ }; - /// }; - /// } - /// }; - /// timerId := ?Timer.recurringTimer(#minutes 30, runFiveTimes); - /// ``` - public let cancelTimer : TimerId -> () = cancel; - -} diff --git a/.mops/core@2.3.1/src/Tuples.mo b/.mops/core@2.3.1/src/Tuples.mo deleted file mode 100644 index 89c5d6c..0000000 --- a/.mops/core@2.3.1/src/Tuples.mo +++ /dev/null @@ -1,365 +0,0 @@ -/// Contains modules for working with tuples of different sizes. -/// -/// Usage example: -/// -/// ```motoko -/// import { Tuple2; Tuple3 } "mo:core/Tuples"; -/// import Bool "mo:core/Bool"; -/// import Nat "mo:core/Nat"; -/// -/// let swapped = Tuple2.swap((1, "hello")); -/// assert swapped == ("hello", 1); -/// let text = Tuple3.toText((1, true, 3), Nat.toText, Bool.toText, Nat.toText); -/// assert text == "(1, true, 3)"; -/// ``` - -import Types "Types"; - -module { - - public module Tuple2 { - /// Swaps the elements of a tuple. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// - /// assert Tuple2.swap((1, "hello")) == ("hello", 1); - /// ``` - public func swap((a, b) : (A, B)) : (B, A) = (b, a); - - /// Creates a textual representation of a tuple for debugging purposes. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// assert Tuple2.toText((1, "hello"), Nat.toText, func (x: Text): Text = x) == "(1, hello)"; - /// ``` - public func toText( - self : (A, B), - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)) - ) : Text = "(" # toTextA(self.0) # ", " # toTextB(self.1) # ")"; - - /// Compares two tuples for equality. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple2.equal((1, "hello"), (1, "hello"), Nat.equal, Text.equal); - /// ``` - public func equal( - self : (A, B), - other : (A, B), - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)) - ) : Bool = equalA(self.0, other.0) and equalB(self.1, other.1); - - /// Compares two tuples lexicographically. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple2.compare((1, "hello"), (1, "world"), Nat.compare, Text.compare) == #less; - /// assert Tuple2.compare((1, "hello"), (2, "hello"), Nat.compare, Text.compare) == #less; - /// assert Tuple2.compare((1, "hello"), (1, "hello"), Nat.compare, Text.compare) == #equal; - /// assert Tuple2.compare((2, "hello"), (1, "hello"), Nat.compare, Text.compare) == #greater; - /// assert Tuple2.compare((1, "world"), (1, "hello"), Nat.compare, Text.compare) == #greater; - /// ``` - public func compare( - self : (A, B), - other : (A, B), - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)) - ) : Types.Order = switch (compareA(self.0, other.0)) { - case (#equal) compareB(self.1, other.1); - case order order - }; - - /// Creates a `toText` function for a tuple given `toText` functions for its elements. - /// This is useful when you need to reuse the same toText conversion multiple times. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// import Nat "mo:core/Nat"; - /// - /// let tupleToText = Tuple2.makeToText(Nat.toText, func x = x); - /// assert tupleToText((1, "hello")) == "(1, hello)"; - /// ``` - public func makeToText( - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)) - ) : ((A, B)) -> Text = func t = toText(t, toTextA, toTextB); - - /// Creates an `equal` function for a tuple given `equal` functions for its elements. - /// This is useful when you need to reuse the same equality comparison multiple times. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let tupleEqual = Tuple2.makeEqual(Nat.equal, Text.equal); - /// assert tupleEqual((1, "hello"), (1, "hello")); - /// ``` - public func makeEqual( - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)) - ) : ((A, B), (A, B)) -> Bool = func(t1, t2) = equal(t1, t2, equalA, equalB); - - /// Creates a `compare` function for a tuple given `compare` functions for its elements. - /// This is useful when you need to reuse the same comparison multiple times. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let tupleCompare = Tuple2.makeCompare(Nat.compare, Text.compare); - /// assert tupleCompare((1, "hello"), (1, "world")) == #less; - /// ``` - public func makeCompare( - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)) - ) : ((A, B), (A, B)) -> Types.Order = func(t1, t2) = compare(t1, t2, compareA, compareB) - }; - - public module Tuple3 { - /// Creates a textual representation of a 3-tuple for debugging purposes. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// assert Tuple3.toText((1, "hello", 2), Nat.toText, func (x: Text): Text = x, Nat.toText) == "(1, hello, 2)"; - /// ``` - public func toText( - self : (A, B, C), - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)), - toTextC : (implicit : (toText : C -> Text)) - ) : Text = "(" # toTextA(self.0) # ", " # toTextB(self.1) # ", " # toTextC(self.2) # ")"; - - /// Compares two 3-tuples for equality. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple3.equal((1, "hello", 2), (1, "hello", 2), Nat.equal, Text.equal, Nat.equal); - /// ``` - public func equal( - self : (A, B, C), - other : (A, B, C), - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)), - equalC : (implicit : (equal : (C, C) -> Bool)) - ) : Bool = equalA(self.0, other.0) and equalB(self.1, other.1) and equalC(self.2, other.2); - - /// Compares two 3-tuples lexicographically. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple3.compare((1, "hello", 2), (1, "world", 1), Nat.compare, Text.compare, Nat.compare) == #less; - /// assert Tuple3.compare((1, "hello", 2), (2, "hello", 2), Nat.compare, Text.compare, Nat.compare) == #less; - /// assert Tuple3.compare((1, "hello", 2), (1, "hello", 2), Nat.compare, Text.compare, Nat.compare) == #equal; - /// assert Tuple3.compare((2, "hello", 2), (1, "hello", 2), Nat.compare, Text.compare, Nat.compare) == #greater; - /// ``` - public func compare( - self : (A, B, C), - other : (A, B, C), - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)), - compareC : (implicit : (compare : (C, C) -> Types.Order)) - ) : Types.Order = switch (compareA(self.0, other.0)) { - case (#equal) { - switch (compareB(self.1, other.1)) { - case (#equal) compareC(self.2, other.2); - case order order - } - }; - case order order - }; - - /// Creates a `toText` function for a 3-tuple given `toText` functions for its elements. - /// This is useful when you need to reuse the same toText conversion multiple times. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// let toText = Tuple3.makeToText(Nat.toText, func x = x, Nat.toText); - /// assert toText((1, "hello", 2)) == "(1, hello, 2)"; - /// ``` - public func makeToText( - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)), - toTextC : (implicit : (toText : C -> Text)) - ) : ((A, B, C)) -> Text = func t = toText(t, toTextA, toTextB, toTextC); - - /// Creates an `equal` function for a 3-tuple given `equal` functions for its elements. - /// This is useful when you need to reuse the same equality comparison multiple times. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// let equal = Tuple3.makeEqual(Nat.equal, Text.equal, Nat.equal); - /// assert equal((1, "hello", 2), (1, "hello", 2)); - /// ``` - public func makeEqual( - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)), - equalC : (implicit : (equal : (C, C) -> Bool)) - ) : ((A, B, C), (A, B, C)) -> Bool = func(t1, t2) = equal(t1, t2, equalA, equalB, equalC); - - /// Creates a `compare` function for a 3-tuple given `compare` functions for its elements. - /// This is useful when you need to reuse the same comparison multiple times. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// let compare = Tuple3.makeCompare(Nat.compare, Text.compare, Nat.compare); - /// assert compare((1, "hello", 2), (1, "world", 1)) == #less; - /// ``` - public func makeCompare( - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)), - compareC : (implicit : (compare : (C, C) -> Types.Order)) - ) : ((A, B, C), (A, B, C)) -> Types.Order = func(t1, t2) = compare(t1, t2, compareA, compareB, compareC) - }; - - public module Tuple4 { - /// Creates a textual representation of a 4-tuple for debugging purposes. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// assert Tuple4.toText((1, "hello", 2, 3), Nat.toText, func (x: Text): Text = x, Nat.toText, Nat.toText) == "(1, hello, 2, 3)"; - /// ``` - public func toText( - self : (A, B, C, D), - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)), - toTextC : (implicit : (toText : C -> Text)), - toTextD : (implicit : (toText : D -> Text)) - ) : Text = "(" # toTextA(self.0) # ", " # toTextB(self.1) # ", " # toTextC(self.2) # ", " # toTextD(self.3) # ")"; - - /// Compares two 4-tuples for equality. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple4.equal((1, "hello", 2, 3), (1, "hello", 2, 3), Nat.equal, Text.equal, Nat.equal, Nat.equal); - /// ``` - public func equal( - self : (A, B, C, D), - other : (A, B, C, D), - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)), - equalC : (implicit : (equal : (C, C) -> Bool)), - equalD : (implicit : (equal : (D, D) -> Bool)) - ) : Bool = equalA(self.0, other.0) and equalB(self.1, other.1) and equalC(self.2, other.2) and equalD(self.3, other.3); - - /// Compares two 4-tuples lexicographically. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple4.compare((1, "hello", 2, 3), (1, "world", 1, 3), Nat.compare, Text.compare, Nat.compare, Nat.compare) == #less; - /// assert Tuple4.compare((1, "hello", 2, 3), (2, "hello", 2, 3), Nat.compare, Text.compare, Nat.compare, Nat.compare) == #less; - /// assert Tuple4.compare((1, "hello", 2, 3), (1, "hello", 2, 3), Nat.compare, Text.compare, Nat.compare, Nat.compare) == #equal; - /// assert Tuple4.compare((2, "hello", 2, 3), (1, "hello", 2, 3), Nat.compare, Text.compare, Nat.compare, Nat.compare) == #greater; - /// ``` - public func compare( - self : (A, B, C, D), - other : (A, B, C, D), - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)), - compareC : (implicit : (compare : (C, C) -> Types.Order)), - compareD : (implicit : (compare : (D, D) -> Types.Order)) - ) : Types.Order = switch (compareA(self.0, other.0)) { - case (#equal) { - switch (compareB(self.1, other.1)) { - case (#equal) { - switch (compareC(self.2, other.2)) { - case (#equal) compareD(self.3, other.3); - case order order - } - }; - case order order - } - }; - case order order - }; - - /// Creates a `toText` function for a 4-tuple given `toText` functions for its elements. - /// This is useful when you need to reuse the same toText conversion multiple times. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// let toText = Tuple4.makeToText(Nat.toText, func (x: Text): Text = x, Nat.toText, Nat.toText); - /// assert toText((1, "hello", 2, 3)) == "(1, hello, 2, 3)"; - /// ``` - public func makeToText( - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)), - toTextC : (implicit : (toText : C -> Text)), - toTextD : (implicit : (toText : D -> Text)) - ) : ((A, B, C, D)) -> Text = func t = toText(t, toTextA, toTextB, toTextC, toTextD); - - /// Creates an `equal` function for a 4-tuple given `equal` functions for its elements. - /// This is useful when you need to reuse the same equality comparison multiple times. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// let equal = Tuple4.makeEqual(Nat.equal, Text.equal, Nat.equal, Nat.equal); - /// assert equal((1, "hello", 2, 3), (1, "hello", 2, 3)); - /// ``` - public func makeEqual( - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)), - equalC : (implicit : (equal : (C, C) -> Bool)), - equalD : (implicit : (equal : (D, D) -> Bool)) - ) : ((A, B, C, D), (A, B, C, D)) -> Bool = func(t1, t2) = equal(t1, t2, equalA, equalB, equalC, equalD); - - /// Creates a `compare` function for a 4-tuple given `compare` functions for its elements. - /// This is useful when you need to reuse the same comparison multiple times. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// let compare = Tuple4.makeCompare(Nat.compare, Text.compare, Nat.compare, Nat.compare); - /// assert compare((1, "hello", 2, 3), (1, "world", 1, 3)) == #less; - /// ``` - public func makeCompare( - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)), - compareC : (implicit : (compare : (C, C) -> Types.Order)), - compareD : (implicit : (compare : (D, D) -> Types.Order)) - ) : ((A, B, C, D), (A, B, C, D)) -> Types.Order = func(t1, t2) = compare(t1, t2, compareA, compareB, compareC, compareD) - } -} diff --git a/.mops/core@2.3.1/src/Types.mo b/.mops/core@2.3.1/src/Types.mo deleted file mode 100644 index 195972f..0000000 --- a/.mops/core@2.3.1/src/Types.mo +++ /dev/null @@ -1,181 +0,0 @@ -/// Common types used throughout the core package. -/// -/// Example usage: -/// -/// ```motoko name=import -/// import { type Result; type Iter } "mo:core/Types"; -/// -/// // Result for error handling -/// let result : Result = #ok(42); -/// -/// // Iterator for sequences -/// let iter : Iter = { next = func() { ?1 } }; -/// ``` - -import Prim "mo:⛔"; - -module { - public type Blob = Prim.Types.Blob; - public type Bool = Prim.Types.Bool; - public type Char = Prim.Types.Char; - public type Error = Prim.Types.Error; - public type ErrorCode = Prim.ErrorCode; - public type Float = Prim.Types.Float; - public type Int = Prim.Types.Int; - public type Int8 = Prim.Types.Int8; - public type Int16 = Prim.Types.Int16; - public type Int32 = Prim.Types.Int32; - public type Int64 = Prim.Types.Int64; - public type Nat = Prim.Types.Nat; - public type Nat8 = Prim.Types.Nat8; - public type Nat16 = Prim.Types.Nat16; - public type Nat32 = Prim.Types.Nat32; - public type Nat64 = Prim.Types.Nat64; - public type Principal = Prim.Types.Principal; - public type Region = Prim.Types.Region; - public type Text = Prim.Types.Text; - - public type Hash = Nat32; - public type Iter = { next : () -> ?T }; - public type Order = { #less; #equal; #greater }; - public type Result = { #ok : T; #err : E }; - public type Pattern = { - #char : Char; - #text : Text; - #predicate : (Char -> Bool) - }; - public type Time = Int; - public type Duration = { - #days : Nat; - #hours : Nat; - #minutes : Nat; - #seconds : Nat; - #milliseconds : Nat; - #nanoseconds : Nat - }; - public type TimerId = Nat; - - public type List = { - var blocks : [var [var ?T]]; - var blockIndex : Nat; - var elementIndex : Nat - }; - - public module Queue { - public type Queue = { - var front : ?Node; - var back : ?Node; - var size : Nat - }; - - public type Node = { - value : T; - var next : ?Node; - var previous : ?Node - } - }; - public type Queue = Queue.Queue; - - public module PriorityQueue { - public type PriorityQueue = { - heap : List - } - }; - public type PriorityQueue = PriorityQueue.PriorityQueue; - - public module Set { - public type Node = { - #leaf : Leaf; - #internal : Internal - }; - - public type Data = { - elements : [var ?T]; - var count : Nat - }; - - public type Internal = { - data : Data; - children : [var ?Node] - }; - - public type Leaf = { - data : Data - }; - - public type Set = { - var root : Node; - var size : Nat - } - }; - public type Set = Set.Set; - - public module Map { - public type Node = { - #leaf : Leaf; - #internal : Internal - }; - - public type Data = { - kvs : [var ?(K, V)]; - var count : Nat - }; - - public type Internal = { - data : Data; - children : [var ?Node] - }; - - public type Leaf = { - data : Data - }; - - public type Map = { - var root : Node; - var size : Nat - } - }; - - public type Map = Map.Map; - - public module Stack { - public type Stack = { - var top : Pure.List; - var size : Nat - } - }; - public type Stack = Stack.Stack; - - public module Pure { - public type List = ?(T, List); - - public module Map { - public type Map = { - size : Nat; - root : Tree - }; - public type Tree = { - #red : (Tree, K, V, Tree); - #black : (Tree, K, V, Tree); - #leaf - }; - - }; - public type Map = Map.Map; - - public type Queue = (List, Nat, List); - - public module Set { - public type Tree = { - #red : (Tree, T, Tree); - #black : (Tree, T, Tree); - #leaf - }; - - public type Set = { size : Nat; root : Tree } - }; - - public type Set = Set.Set; - - } -} diff --git a/.mops/core@2.3.1/src/VarArray.mo b/.mops/core@2.3.1/src/VarArray.mo deleted file mode 100644 index 7dac8c0..0000000 --- a/.mops/core@2.3.1/src/VarArray.mo +++ /dev/null @@ -1,1407 +0,0 @@ -/// Provides extended utility functions on mutable Arrays (`[var]`). -/// -/// Note the difference between mutable (`[var]`) and immutable (`[]`) arrays. -/// Mutable arrays allow their elements to be modified after creation, while -/// immutable arrays are fixed once created. -/// -/// WARNING: If you are looking for a list that can grow and shrink in size, -/// it is recommended you use `List` for those purposes. -/// Arrays must be created with a fixed size. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import VarArray "mo:core/VarArray"; -/// ``` - -import Types "Types"; -import Order "Order"; -import Result "Result"; -import Option "Option"; -import Prim "mo:⛔"; -import InsertionSort "internal/SortHelper"; - -module { - let nat = Prim.nat32ToNat; - - /// Creates an empty mutable array (equivalent to `[var]`). - /// - /// ```motoko include=import - /// let array = VarArray.empty(); - /// assert array.size() == 0; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func empty() : [var T] = [var]; - - /// Creates a mutable array containing `item` repeated `size` times. - /// - /// ```motoko include=import - /// import Text "mo:core/Text"; - /// - /// let array = VarArray.repeat("Echo", 3); - /// assert VarArray.equal(array, [var "Echo", "Echo", "Echo"], Text.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func repeat(item : T, size : Nat) : [var T] = Prim.Array_init(size, item); - - /// Duplicates `array`, returning a shallow copy of the original. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array1 = [var 1, 2, 3]; - /// let array2 = VarArray.clone(array1); - /// array2[0] := 0; - /// assert VarArray.equal(array1, [var 1, 2, 3], Nat.equal); - /// assert VarArray.equal(array2, [var 0, 2, 3], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func clone(self : [var T]) : [var T] = Prim.Array_tabulateVar(self.size(), func i = self[i]); - - /// Creates a mutable array of size `size`. Each element at index i - /// is created by applying `generator` to i. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array : [var Nat] = VarArray.tabulate(4, func i = i * 2); - /// assert VarArray.equal(array, [var 0, 2, 4, 6], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `generator` runs in O(1) time and space. - public let tabulate : (size : Nat, generator : Nat -> T) -> [var T] = Prim.Array_tabulateVar; - - /// Tests if two arrays contain equal values (i.e. they represent the same - /// list of elements). Uses `equal` to compare elements in the arrays. - /// - /// ```motoko include=import - /// // Use the equal function from the Nat module to compare Nats - /// import Nat "mo:core/Nat"; - /// - /// let array1 = [var 0, 1, 2, 3]; - /// let array2 = [var 0, 1, 2, 3]; - /// assert VarArray.equal(array1, array2, Nat.equal); - /// ``` - /// - /// Runtime: O(size1 + size2) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func equal(self : [var T], other : [var T], equal : (implicit : (T, T) -> Bool)) : Bool { - let size1 = self.size(); - let size2 = other.size(); - if (size1 != size2) { - return false - }; - var i = 0; - while (i < size1) { - if (not equal(self[i], other[i])) { - return false - }; - i += 1 - }; - true - }; - - /// Returns the first value in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let array = [var 1, 9, 4, 8]; - /// let found = VarArray.find(array, func x = x > 8); - /// assert found == ?9; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func find(self : [var T], predicate : T -> Bool) : ?T { - for (element in self.vals()) { - if (predicate element) { - return ?element - } - }; - null - }; - - /// Returns the first index in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let array = [var 'A', 'B', 'C', 'D']; - /// let found = VarArray.findIndex(array, func(x) { x == 'C' }); - /// assert found == ?2; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func findIndex(self : [var T], predicate : T -> Bool) : ?Nat { - for ((index, element) in enumerate(self)) { - if (predicate element) { - return ?index - } - }; - null - }; - - /// Create a new mutable array by concatenating the values of `array1` and `array2`. - /// Note that `VarArray.concat` copies its arguments and has linear complexity. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array1 = [var 1, 2, 3]; - /// let array2 = [var 4, 5, 6]; - /// let result = VarArray.concat(array1, array2); - /// assert VarArray.equal(result, [var 1, 2, 3, 4, 5, 6], Nat.equal); - /// ``` - /// Runtime: O(size1 + size2) - /// - /// Space: O(size1 + size2) - public func concat(self : [var T], other : [var T]) : [var T] { - let size1 = self.size(); - let size2 = other.size(); - tabulate( - size1 + size2, - func i { - if (i < size1) { - self[i] - } else { - other[i - size1] - } - } - ) - }; - - /// Creates a new sorted copy of the mutable array according to `compare`. - /// Sort is deterministic and stable. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 4, 2, 6]; - /// let sorted = VarArray.sort(array, Nat.compare); - /// assert VarArray.equal(sorted, [var 2, 4, 6], Nat.equal); - /// ``` - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sort(self : [var T], compare : (implicit : (T, T) -> Order.Order)) : [var T] { - let newArray = clone(self); - sortInPlace(newArray, compare); - newArray - }; - - /// Sorts the elements in a mutable array in place according to `compare`. - /// Sort is deterministic and stable. This modifies the original array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 4, 2, 6]; - /// VarArray.sortInPlace(array, Nat.compare); - /// assert VarArray.equal(array, [var 2, 4, 6], Nat.equal); - /// ``` - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sortInPlace(self : [var T], compare : (implicit : (T, T) -> Order.Order)) : () { - let size = Prim.natToNat32(self.size()); - if (size <= 1) return; - if (size <= 8) { - InsertionSort.insertionSortSmall(self, self, compare, 0 : Nat32, size); - return - }; - let buffer = repeat(self[0], nat(size / 2)); - mergeSortRec(self, buffer, compare, 0 : Nat32, size, true, 0 : Nat32) - }; - - // input data is alwways in array - // even: write output data to array in place - // odd: write output data to buffer at offset - // offset is only used when odd - func mergeSortRec( - array : [var T], - buffer : [var T], - compare : (T, T) -> Order.Order, - from : Nat32, - to : Nat32, - even : Bool, - offset : Nat32 - ) { - debug assert from < to; - let size = to -% from; - debug assert size >= 4; - - if (size <= 8) { - if (even) { - InsertionSort.insertionSortSmall(array, array, compare, from, size); // sorts array in place - } else { - InsertionSort.insertionSortSmallMove(array, buffer, compare, from, size, offset); // sorts to buffer at offset - }; - return - }; - - let len1 = size / 2; - let mid = from +% len1; - if (even) { - // merge to array in place - mergeSortRec(array, buffer, compare, mid, to, true, 0 : Nat32); // sort upper half to array in place - mergeSortRec(array, buffer, compare, from, mid, false, 0 : Nat32); // sort lower half to beginning of buffer - merge1(array, buffer, compare, from, mid, to); // merge to array in place - } else { - // merge to buffer at offset - mergeSortRec(array, buffer, compare, from, mid, true, 0 : Nat32); // lower half to array in place - mergeSortRec(array, buffer, compare, mid, to, false, offset +% len1); // sort upper half to buffer starting shifted offset - merge2(array, buffer, compare, from, mid, size, offset); // merge to buffer at offset - } - }; - - func merge1(array : [var T], buffer : [var T], compare : (T, T) -> Order.Order, from : Nat32, mid : Nat32, to : Nat32) { - debug assert from < mid; - debug assert mid < to; - let len = mid -% from; - var pos = from; - var i = 0 : Nat32; - var j = mid; - - var iElem = buffer[nat(i)]; - var jElem = array[nat(j)]; - label L loop { - switch (compare(jElem, iElem)) { - case (#less) { - array[nat(pos)] := jElem; - j +%= 1; - pos +%= 1; - if (j == to) { - while (i < len) { - array[nat(pos)] := buffer[nat(i)]; - i +%= 1; - pos +%= 1 - }; - break L - }; - jElem := array[nat(j)] - }; - case (_) { - array[nat(pos)] := iElem; - i +%= 1; - pos +%= 1; - if (i == len) break L; - iElem := buffer[nat(i)] - } - } - } - }; - - func merge2(array : [var T], buffer : [var T], compare : (T, T) -> Order.Order, from : Nat32, mid : Nat32, size : Nat32, offset : Nat32) { - debug assert from < mid; - debug assert mid < from +% size; - let len = mid -% from; - var pos = offset; - var i = from; - var j = offset +% len; - let j_max = offset +% size; - - var iElem = array[nat(i)]; - var jElem = buffer[nat(j)]; - label L loop { - switch (compare(jElem, iElem)) { - case (#less) { - buffer[nat(pos)] := jElem; - j +%= 1; - pos +%= 1; - if (j == j_max) { - while (i < mid) { - buffer[nat(pos)] := array[nat(i)]; - i +%= 1; - pos +%= 1 - }; - break L - }; - jElem := buffer[nat(j)] - }; - case (_) { - buffer[nat(pos)] := iElem; - i +%= 1; - pos +%= 1; - if (i == mid) break L; - iElem := array[nat(i)] - } - } - } - }; - - /// Creates a new mutable array by reversing the order of elements in `array`. - /// The original array is not modified. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 10, 11, 12]; - /// let reversed = VarArray.reverse(array); - /// assert VarArray.equal(reversed, [var 12, 11, 10], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func reverse(self : [var T]) : [var T] { - let size = self.size(); - tabulate(size, func i = self[size - i - 1]) - }; - - /// Reverses the order of elements in a mutable array in place. - /// This modifies the original array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 10, 11, 12]; - /// VarArray.reverseInPlace(array); - /// assert VarArray.equal(array, [var 12, 11, 10], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func reverseInPlace(self : [var T]) : () { - let size = self.size(); - if (size == 0) { - return - }; - var i = 0; - var j = (size - 1) : Nat; - while (i < j) { - let temp = self[i]; - self[i] := self[j]; - self[j] := temp; - i += 1; - j -= 1 - } - }; - - /// Calls `f` with each element in `array`. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// var sum = 0; - /// let array = [var 0, 1, 2, 3]; - /// VarArray.forEach(array, func(x) { - /// sum += x; - /// }); - /// assert sum == 6; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEach(self : [var T], f : T -> ()) { - for (item in self.vals()) { - f(item) - } - }; - - /// Creates a new mutable array by applying `f` to each element in `array`. `f` "maps" - /// each element it is applied to of type `T` to an element of type `R`. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 0, 1, 2, 3]; - /// let array2 = VarArray.map(array, func x = x * 2); - /// assert VarArray.equal(array2, [var 0, 2, 4, 6], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func map(self : [var T], f : T -> R) : [var R] { - tabulate( - self.size(), - func(index) { - f(self[index]) - } - ) - }; - - /// Applies `f` to each element of `array` in place, - /// retaining the original ordering of elements. - /// This modifies the original array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 0, 1, 2, 3]; - /// VarArray.mapInPlace(array, func x = x * 3); - /// assert VarArray.equal(array, [var 0, 3, 6, 9], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapInPlace(self : [var T], f : T -> T) { - var index = 0; - let size = self.size(); - while (index < size) { - self[index] := f(self[index]); - index += 1 - } - }; - - /// Creates a new mutable array by applying `predicate` to every element - /// in `array`, retaining the elements for which `predicate` returns true. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 4, 2, 6, 1, 5]; - /// let evenElements = VarArray.filter(array, func x = x % 2 == 0); - /// assert VarArray.equal(evenElements, [var 4, 2, 6], Nat.equal); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func filter(self : [var T], f : T -> Bool) : [var T] { - var count = 0; - let keep = Prim.Array_tabulate( - self.size(), - func i { - if (f(self[i])) { - count += 1; - true - } else { - false - } - } - ); - var nextKeep = 0; - tabulate( - count, - func _ { - while (not keep[nextKeep]) { - nextKeep += 1 - }; - nextKeep += 1; - self[nextKeep - 1] - } - ) - }; - - /// Creates a new mutable array by applying `f` to each element in `array`, - /// and keeping all non-null elements. The ordering is retained. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let array = [var 4, 2, 0, 1]; - /// let newArray = - /// VarArray.filterMap( // mapping from Nat to Text values - /// array, - /// func x = if (x == 0) { null } else { ?Nat.toText(100 / x) } // can't divide by 0, so return null - /// ); - /// assert VarArray.equal(newArray, [var "25", "50", "100"], Text.equal); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func filterMap(self : [var T], f : T -> ?R) : [var R] { - var count = 0; - let options = Prim.Array_tabulate( - self.size(), - func i { - let result = f(self[i]); - switch (result) { - case (?element) { - count += 1; - result - }; - case null { - null - } - } - } - ); - - var nextSome = 0; - tabulate( - count, - func _ { - while (Option.isNull(options[nextSome])) { - nextSome += 1 - }; - nextSome += 1; - switch (options[nextSome - 1]) { - case (?element) element; - case null { - Prim.trap "VarArray.filterMap(): malformed array" - } - } - } - ) - }; - - /// Creates a new mutable array by applying `f` to each element in `array`. - /// If any invocation of `f` produces an `#err`, returns an `#err`. Otherwise - /// returns an `#ok` containing the new array. - /// - /// ```motoko include=import - /// import Result "mo:core/Result"; - /// - /// let array = [var 4, 3, 2, 1, 0]; - /// // divide 100 by every element in the array - /// let result = VarArray.mapResult(array, func x { - /// if (x > 0) { - /// #ok(100 / x) - /// } else { - /// #err "Cannot divide by zero" - /// } - /// }); - /// assert Result.isErr(result); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - /// @deprecated M0235 - public func mapResult(self : [var T], f : T -> Result.Result) : Result.Result<[var R], E> { - let size = self.size(); - - var error : ?Result.Result<[var R], E> = null; - let results = tabulate( - size, - func i { - switch (f(self[i])) { - case (#ok element) { - ?element - }; - case (#err e) { - switch (error) { - case null { - // only take the first error - error := ?(#err e) - }; - case _ {} - }; - null - } - } - } - ); - - switch error { - case null { - // unpack the option - #ok( - map( - results, - func element { - switch element { - case (?element) { - element - }; - case null { - Prim.trap "VarArray.mapResults(): malformed array" - } - } - } - ) - ) - }; - case (?error) { - error - } - } - }; - - /// Creates a new array by applying `f` to each element in `array` and its index. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 10, 10, 10, 10]; - /// let newArray = VarArray.mapEntries(array, func (x, i) = i * x); - /// assert VarArray.equal(newArray, [var 0, 10, 20, 30], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapEntries(self : [var T], f : (T, Nat) -> R) : [var R] { - tabulate(self.size(), func i = f(self[i], i)) - }; - - /// Creates a new mutable array by applying `k` to each element in `array`, - /// and concatenating the resulting arrays in order. - /// - /// ```motoko include=import - /// import Int "mo:core/Int" - /// - /// let array = [var 1, 2, 3, 4]; - /// let newArray = VarArray.flatMap(array, func x = [x, -x].vals()); - /// assert VarArray.equal(newArray, [var 1, -1, 2, -2, 3, -3, 4, -4], Int.equal); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `k` runs in O(1) time and space. - public func flatMap(self : [var T], k : T -> Types.Iter) : [var R] { - var flatSize = 0; - let arrays = Prim.Array_tabulate<[var R]>( - self.size(), - func i { - let subArray = fromIter(k(self[i])); // TODO: optimize - flatSize += subArray.size(); - subArray - } - ); - - // could replace with a call to flatten, - // but it would require an extra pass (to compute `flatSize`) - var outer = 0; - var inner = 0; - tabulate( - flatSize, - func _ { - while (inner == arrays[outer].size()) { - inner := 0; - outer += 1 - }; - let element = arrays[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// ```motoko include=import - /// import {add} "mo:core/Nat"; - /// - /// let array = [var 4, 2, 0, 1]; - /// let sum = - /// VarArray.foldLeft( - /// array, - /// 0, // start the sum at 0 - /// func(sumSoFar, x) = sumSoFar + x // this entire function can be replaced with `add`! - /// ); - /// assert sum == 7; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(self : [var T], base : A, combine : (A, T) -> A) : A { - var acc = base; - for (element in self.vals()) { - acc := combine(acc, element) - }; - acc - }; - - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// ```motoko include=import - /// import {toText} "mo:core/Nat"; - /// - /// let array = [var 1, 9, 4, 8]; - /// let bookTitle = VarArray.foldRight(array, "", func(x, acc) = toText(x) # acc); - /// assert bookTitle == "1948"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldRight(self : [var T], base : A, combine : (T, A) -> A) : A { - var acc = base; - let size = self.size(); - var i = size; - while (i > 0) { - i -= 1; - acc := combine(self[i], acc) - }; - acc - }; - - /// Combines an iterator of mutable arrays into a single mutable array. - /// Retains the original ordering of the elements. - /// - /// Consider using `VarArray.flatten()` for better performance. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let arrays : [[var Nat]] = [[var 0, 1, 2], [var 2, 3], [var], [var 4]]; - /// let joinedArray = VarArray.join(arrays.vals()); - /// assert VarArray.equal(joinedArray, [var 0, 1, 2, 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(number of elements in array) - /// - /// Space: O(number of elements in array) - public func join(self : Types.Iter<[var T]>) : [var T] { - flatten(fromIter(self)) - }; - - /// Combines a mutable array of mutable arrays into a single mutable array. Retains the original - /// ordering of the elements. - /// - /// This has better performance compared to `VarArray.join()`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let arrays : [var [var Nat]] = [var [var 0, 1, 2], [var 2, 3], [var], [var 4]]; - /// let flatArray = VarArray.flatten(arrays); - /// assert VarArray.equal(flatArray, [var 0, 1, 2, 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(number of elements in array) - /// - /// Space: O(number of elements in array) - public func flatten(self : [var [var T]]) : [var T] { - var flatSize = 0; - for (subArray in self.vals()) { - flatSize += subArray.size() - }; - - var outer = 0; - var inner = 0; - tabulate( - flatSize, - func _ { - while (inner == self[outer].size()) { - inner := 0; - outer += 1 - }; - let element = self[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Create an array containing a single value. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = VarArray.singleton(2); - /// assert VarArray.equal(array, [var 2], Nat.equal); - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func singleton(element : T) : [var T] = [var element]; - - /// Returns the size of a mutable array. Equivalent to `array.size()`. - public func size(self : [var T]) : Nat = self.size(); - - /// Returns whether a mutable array is empty, i.e. contains zero elements. - public func isEmpty(self : [var T]) : Bool = self.size() == 0; - - /// Transforms an immutable array into a mutable array. - /// - /// ```motoko include=import - /// let array = [0, 1, 2]; - /// let varArray = VarArray.fromArray(array); - /// assert varArray.size() == 3; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// @deprecated M0235 - public func fromArray(array : [T]) : [var T] = Prim.Array_tabulateVar(array.size(), func i = array[i]); - - /// Converts an iterator to a mutable array. - public func fromIter(iter : Types.Iter) : [var T] { - var list : Types.Pure.List = null; - var size = 0; - label l loop { - switch (iter.next()) { - case (?element) { - list := ?(element, list); - size += 1 - }; - case null { break l } - } - }; - if (size == 0) { return [var] }; - let array = Prim.Array_init( - size, - switch list { - case (?(h, _)) h; - case null { - Prim.trap("VarArray.fromIter(): unreachable") - } - } - ); - var i = size; - while (i > 0) { - i -= 1; - switch list { - case (?(h, t)) { - array[i] := h; - list := t - }; - case null { - Prim.trap("VarArray.fromIter(): unreachable") - } - } - }; - array - }; - - /// Returns an iterator (`Iter`) over the indices of `array`. - /// An iterator provides a single method `next()`, which returns - /// indices in order, or `null` when out of index to iterate over. - /// - /// NOTE: You can also use `array.keys()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// let array = [var 10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.keys()) { - /// sum += element; - /// }; - /// assert sum == 3; // 0 + 1 + 2 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func keys(self : [var T]) : Types.Iter = self.keys(); - - /// Iterator provides a single method `next()`, which returns - /// elements in order, or `null` when out of elements to iterate over. - /// - /// Note: You can also use `array.values()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// let array = [var 10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.values()) { - /// sum += element; - /// }; - /// assert sum == 33; // 10 + 11 + 12 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func values(self : [var T]) : Types.Iter = self.vals(); - - /// Returns an iterator that provides pairs of (index, element) in order, or `null` - /// when out of elements to iterate over. - /// - /// ```motoko include=import - /// let array = [var 10, 11, 12]; - /// - /// var sum = 0; - /// for ((index, element) in VarArray.enumerate(array)) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func enumerate(self : [var T]) : Types.Iter<(Nat, T)> = object { - let size = self.size(); - var index = 0; - public func next() : ?(Nat, T) { - if (index >= size) { - return null - }; - let i = index; - index += 1; - ?(i, self[i]) - } - }; - - /// Returns true if all elements in `array` satisfy the predicate function. - /// - /// ```motoko include=import - /// let array = [var 1, 2, 3, 4]; - /// assert VarArray.all(array, func x = x > 0); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func all(self : [var T], predicate : T -> Bool) : Bool { - for (element in self.values()) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Returns true if any element in `array` satisfies the predicate function. - /// - /// ```motoko include=import - /// let array = [var 1, 2, 3, 4]; - /// assert VarArray.any(array, func x = x > 3); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func any(self : [var T], predicate : T -> Bool) : Bool { - for (element in self.values()) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Returns the index of the first `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.indexOf(array, Char.equal, 'c') == ?0; - /// assert VarArray.indexOf(array, Char.equal, 'f') == ?2; - /// assert VarArray.indexOf(array, Char.equal, 'g') == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func indexOf(self : [var T], equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = nextIndexOf(self, equal, element, 0); - - /// Returns the index of the next occurence of `element` in the `array` starting from the `from` index (inclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.nextIndexOf(array, Char.equal, 'c', 0) == ?0; - /// assert VarArray.nextIndexOf(array, Char.equal, 'f', 0) == ?2; - /// assert VarArray.nextIndexOf(array, Char.equal, 'f', 2) == ?2; - /// assert VarArray.nextIndexOf(array, Char.equal, 'f', 3) == ?3; - /// assert VarArray.nextIndexOf(array, Char.equal, 'f', 4) == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func nextIndexOf(self : [var T], equal : (implicit : (T, T) -> Bool), element : T, fromInclusive : Nat) : ?Nat { - var index = fromInclusive; - let size = self.size(); - while (index < size) { - if (equal(self[index], element)) { - return ?index - } else { - index += 1 - } - }; - null - }; - - /// Returns the index of the last `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.lastIndexOf(array, Char.equal, 'c') == ?0; - /// assert VarArray.lastIndexOf(array, Char.equal, 'f') == ?3; - /// assert VarArray.lastIndexOf(array, Char.equal, 'e') == ?5; - /// assert VarArray.lastIndexOf(array, Char.equal, 'g') == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func lastIndexOf(self : [var T], equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = prevIndexOf(self, equal, element, self.size()); - - /// Returns the index of the previous occurence of `element` in the `array` starting from the `from` index (exclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.prevIndexOf(array, Char.equal, 'c', array.size()) == ?0; - /// assert VarArray.prevIndexOf(array, Char.equal, 'e', array.size()) == ?5; - /// assert VarArray.prevIndexOf(array, Char.equal, 'e', 5) == ?4; - /// assert VarArray.prevIndexOf(array, Char.equal, 'e', 4) == null; - /// ``` - /// - /// Runtime: O(array.size()); - /// Space: O(1); - public func prevIndexOf(self : [var T], equal : (implicit : (T, T) -> Bool), element : T, fromExclusive : Nat) : ?Nat { - var i = fromExclusive; - while (i > 0) { - i -= 1; - if (equal(self[i], element)) { - return ?i - } - }; - null - }; - - /// Returns true if the `array` contains `element` using the provided `equal` function. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.contains(array, Char.equal, 'f'); - /// assert not VarArray.contains(array, Char.equal, 'g'); - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func contains(self : [var T], equal : (implicit : (T, T) -> Bool), element : T) : Bool { - for (item in self.vals()) { - if (equal(item, element)) { - return true - } - }; - false - }; - - /// Returns an iterator over a slice of `array` starting at `fromInclusive` up to (but not including) `toExclusive`. - /// - /// Negative indices are relative to the end of the array. For example, `-1` corresponds to the last element in the array. - /// - /// If the indices are out of bounds, they are clamped to the array bounds. - /// If the first index is greater than the second, the function returns an empty iterator. - /// - /// ```motoko include=import - /// let array = [var 1, 2, 3, 4, 5]; - /// let iter1 = VarArray.range(array, 3, array.size()); - /// assert iter1.next() == ?4; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// let iter2 = VarArray.range(array, 3, -1); - /// assert iter2.next() == ?4; - /// assert iter2.next() == null; - /// - /// let iter3 = VarArray.range(array, 0, 0); - /// assert iter3.next() == null; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func range(self : [var T], fromInclusive : Int, toExclusive : Int) : Types.Iter { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (values are non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - object { - var pos = start; - public func next() : ?T { - if (pos >= end) { - null - } else { - let elem = self[pos]; - pos += 1; - ?elem - } - } - } - }; - - /// Returns a new array containing elements from `array` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// let array = [var 1, 2, 3, 4, 5]; - /// - /// let slice1 = VarArray.sliceToArray(array, 1, 4); - /// assert slice1 == [2, 3, 4]; - /// - /// let slice2 = VarArray.sliceToArray(array, 1, -1); - /// assert slice2 == [2, 3, 4]; - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToArray(self : [var T], fromInclusive : Int, toExclusive : Int) : [T] { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - if (start >= end) { - return [] - }; - Prim.Array_tabulate(end - start, func i = self[start + i]) - }; - - /// Returns a new mutable array containing elements from `array` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 1, 2, 3, 4, 5]; - /// - /// let slice1 = VarArray.sliceToVarArray(array, 1, 4); - /// assert VarArray.equal(slice1, [var 2, 3, 4], Nat.equal); - /// - /// let slice2 = VarArray.sliceToVarArray(array, 1, -1); - /// assert VarArray.equal(slice2, [var 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToVarArray(self : [var T], fromInclusive : Int, toExclusive : Int) : [var T] { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - if (start >= end) { - return [var] - }; - Prim.Array_tabulateVar(end - start, func i = self[start + i]) - }; - - /// Transforms a mutable array into an immutable array. - /// - /// ```motoko include=import - /// let varArray = [var 0, 1, 2]; - /// varArray[2] := 3; - /// let array = VarArray.toArray(varArray); - /// assert array == [0, 1, 3]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func toArray(self : [var T]) : [T] = Prim.Array_tabulate(self.size(), func i = self[i]); - - /// Converts the mutable array to its textual representation using `f` to convert each element to `Text`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 1, 2, 3]; - /// assert VarArray.toText(array, Nat.toText) == "[var 1, 2, 3]"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func toText(self : [var T], f : (implicit : (toText : T -> Text))) : Text { - let size = self.size(); - if (size == 0) { return "[var]" }; - var text = "[var "; - var i = 0; - while (i < size) { - if (i != 0) { - text #= ", " - }; - text #= f(self[i]); - i += 1 - }; - text #= "]"; - text - }; - - /// Compares two mutable arrays using the provided comparison function for elements. - /// Returns #less, #equal, or #greater if `array1` is less than, equal to, - /// or greater than `array2` respectively. - /// - /// If arrays have different sizes but all elements up to the shorter length are equal, - /// the shorter array is considered #less than the longer array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// let array1 = [var 1, 2, 3]; - /// let array2 = [var 1, 2, 4]; - /// assert VarArray.compare(array1, array2, Nat.compare) == #less; - /// - /// let array3 = [var 1, 2]; - /// let array4 = [var 1, 2, 3]; - /// assert VarArray.compare(array3, array4, Nat.compare) == #less; - /// ``` - /// - /// Runtime: O(min(size1, size2)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func compare(self : [var T], other : [var T], compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let size1 = self.size(); - let size2 = other.size(); - var i = 0; - let minSize = if (size1 < size2) { size1 } else { size2 }; - while (i < minSize) { - switch (compare(self[i], other[i])) { - case (#less) { return #less }; - case (#greater) { return #greater }; - case (#equal) { i += 1 } - } - }; - if (size1 < size2) { #less } else if (size1 > size2) { #greater } else { - #equal - } - }; - - /// Performs binary search on a sorted mutable array to find the index of the `element`. - /// Returns `#found(index)` if the element is found, or `#insertionIndex(index)` with the index - /// - /// If there are multiple equal elements, no guarantee is made about which index is returned. - /// The array must be sorted in ascending order according to the `compare` function. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let sorted = [var 1, 3, 5, 7, 9, 11]; - /// assert VarArray.binarySearch(sorted, Nat.compare, 5) == #found(2); - /// assert VarArray.binarySearch(sorted, Nat.compare, 6) == #insertionIndex(3); - /// ``` - /// - /// Runtime: O(log(size)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func binarySearch(self : [var T], compare : (implicit : (T, T) -> Order.Order), element : T) : { - #found : Nat; - #insertionIndex : Nat - } { - var left = 0; - var right = self.size(); - while (left < right) { - let mid = (left + right) / 2; - switch (compare(self[mid], element)) { - case (#less) left := mid + 1; - case (#greater) right := mid; - case (#equal) return #found mid - } - }; - #insertionIndex left - }; - - /// Checks whether the mutable `array` is sorted according to the `compare` function. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 1, 2, 3]; - /// assert VarArray.isSorted(array, Nat.compare); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func isSorted(self : [var T], compare : (implicit : (T, T) -> Order.Order)) : Bool { - let size = self.size(); - if (size <= 1) return true; - var i = 1; - while (i < size) { - switch (compare(self[i - 1], self[i])) { - case (#greater) return false; - case _ { i += 1 } - } - }; - true - } - -} diff --git a/.mops/core@2.3.1/src/WeakReference.mo b/.mops/core@2.3.1/src/WeakReference.mo deleted file mode 100644 index a7c4a69..0000000 --- a/.mops/core@2.3.1/src/WeakReference.mo +++ /dev/null @@ -1,59 +0,0 @@ -/// Module that implements a weak reference to an object. -/// -/// ATTENTION: This functionality does not work with classical persistence (`--legacy-persistence` moc flag). -/// -/// Usage example: -/// Import from the core package to use this module. -/// ```motoko name=import -/// import WeakReference "mo:core/WeakReference"; -/// ``` - -import Prim "mo:⛔" - -module { - public type WeakReference = { - ref : weak T - }; - - /// Allocate a new weak reference to the given object. - /// - /// The `obj` parameter is the object to allocate a weak reference for. - /// Returns a new weak reference pointingto the given object. - /// ```motoko include=import - /// let obj = { x = 1 }; - /// let weakRef = WeakReference.allocate(obj); - /// ``` - public func allocate(obj : T) : WeakReference { - return { ref = Prim.allocWeakRef(obj) } - }; - - /// Get the value that the weak reference is pointing to. - /// - /// The `self` parameter is the weak reference pointing to the value the function returns. - /// The function returns the value that the weak reference is pointing to, - /// or `null` if the value has been collected by the garbage collector. - /// ```motoko include=import - /// let obj = { x = 1 }; - /// let weakRef = WeakReference.allocate(obj); - /// let value = weakRef.get(); - /// ``` - public func get(self : WeakReference) : ?T { - return Prim.weakGet(self.ref) - }; - - /// Check if the weak reference is still alive. - /// - /// The `self` parameter is the weak reference to check whether it is still alive. - /// Returns `true` if the weak reference is still alive, `false` otherwise. - /// False means that the value has been collected by the garbage collector. - /// ```motoko include=import - /// let obj = { x = 1 }; - /// let weakRef = WeakReference.allocate(obj); - /// let isLive = weakRef.isLive(); - /// assert isLive == true; - /// ``` - public func isLive(self : WeakReference) : Bool { - return Prim.isLive(self.ref) - }; - -} diff --git a/.mops/core@2.3.1/src/internal/BTreeHelper.mo b/.mops/core@2.3.1/src/internal/BTreeHelper.mo deleted file mode 100644 index 888087d..0000000 --- a/.mops/core@2.3.1/src/internal/BTreeHelper.mo +++ /dev/null @@ -1,412 +0,0 @@ -// Implementation is courtesy of Byron Becker. -// Source: https://github.com/canscale/StableHeapBTreeMap -// Copyright (c) 2022 Byron Becker. -// Distributed under Apache 2.0 license. -// With adjustments by the Motoko team. - -import VarArray "../VarArray"; -import Runtime "../Runtime"; - -module { - /// Inserts an element into a mutable array at a specific index, shifting all other elements over - /// - /// Parameters: - /// - /// array - the array being inserted into - /// insertElement - the element being inserted - /// insertIndex - the index at which the element will be inserted - /// currentLastElementIndex - the index of last **non-null** element in the array (used to start shifting elements over) - /// - /// Note: This assumes that there are nulls at the end of the array and that the array is not full. - /// If the array is already full, this function will overflow the array size when attempting to - /// insert and will cause the cansiter to trap - public func insertAtPosition(array : [var ?T], insertElement : ?T, insertIndex : Nat, currentLastElementIndex : Nat) { - // if inserting at the end of the array, don't need to do any shifting and can just insert and return - if (insertIndex == currentLastElementIndex + 1) { - array[insertIndex] := insertElement; - return - }; - - // otherwise, need to shift all of the elements at the end of the array over one by one until - // the insert index is hit. - var j = currentLastElementIndex; - label l loop { - array[j + 1] := array[j]; - if (j == insertIndex) { - array[j] := insertElement; - break l - }; - - j -= 1 - } - }; - - /// Splits the array into two halves as if the insert has occured, omitting the middle element and returning it so that it can - /// be promoted to the parent internal node. This is used when inserting an element into an array of elements that - /// is already full. - /// - /// Note: Use only when inserting an element into a FULL array & promoting the resulting midpoint element. - /// This is NOT the same as just splitting this array! - /// - /// Parameters: - /// - /// array - the array being split - /// insertElement - the element being inserted - /// insertIndex - the position/index that the insertElement should be inserted - public func insertOneAtIndexAndSplitArray(array : [var ?T], insertElement : T, insertIndex : Nat) : ([var ?T], T, [var ?T]) { - // split at the BTree order / 2 - let splitIndex = (array.size() + 1) / 2; - // this function assumes the the splitIndex is in the middle of the kvs array - trap otherwise - if (splitIndex > array.size()) { assert false }; - - let leftSplit = if (insertIndex < splitIndex) { - VarArray.tabulate( - array.size(), - func(i) { - // if below the split index - if (i < splitIndex) { - // if below the insert index, copy over - if (i < insertIndex) { array[i] } - // if less than the insert index, copy over the previous element (since the inserted element has taken up 1 extra slot) - else if (i > insertIndex) { array[i - 1] } - // if equal to the insert index add the element to be inserted to the left split - else { ?insertElement } - } else { null } - } - ) - } - // index >= splitIndex - else { - VarArray.tabulate( - array.size(), - func(i) { - // right biased splitting - if (i < splitIndex) { array[i] } else { null } - } - ) - }; - - let (rightSplit, middleElement) : ([var ?T], ?T) = - // if insert > split index, inserted element will be inserted into the right split - if (insertIndex > splitIndex) { - let right = VarArray.tabulate( - array.size(), - func(i) { - let adjIndex = i + splitIndex + 1; // + 1 accounts for the fact that the split element was part of the original array - if (adjIndex <= array.size()) { - if (adjIndex < insertIndex) { array[adjIndex] } else if (adjIndex > insertIndex) { - array[adjIndex - 1] - } else { ?insertElement } - } else { null } - } - ); - (right, array[splitIndex]) - } - // if inserted element was placed in the left split - else if (insertIndex < splitIndex) { - let right = VarArray.tabulate( - array.size(), - func(i) { - let adjIndex = i + splitIndex; - if (adjIndex < array.size()) { array[adjIndex] } else { null } - } - ); - (right, array[splitIndex - 1]) - } - // insertIndex == splitIndex - else { - let right = VarArray.tabulate( - array.size(), - func(i) { - let adjIndex = i + splitIndex; - if (adjIndex < array.size()) { array[adjIndex] } else { null } - } - ); - (right, ?insertElement) - }; - - switch (middleElement) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: insertOneAtIndexAndSplitArray, middle element of a BTree node should never be null") - }; - case (?el) { (leftSplit, el, rightSplit) } - } - }; - - /// Context of use: This function is used after inserting a child node into the full child of an internal node that is also full. - /// From the insertion, the full child is rebalanced and split, and then since the internal node is full, when replacing the two - /// halves of that rebalanced child into the internal node's children this causes a second split. This function takes in the - /// internal node's children, and the "rebalanced" split child nodes, as well as the index at which the "rebalanced" left and right - /// child will be inserted and replaces the original child with those two halves - /// - /// Note: Use when inserting two successive elements into a FULL array and splitting that array. - /// This is NOT the same as just splitting this array! - /// - /// Assumptions: this function also assumes that the children array is full (no nulls) - /// - /// Parameters: - /// - /// children - the internal node's children array being split - /// rebalancedChildIndex - the index used to mark where the rebalanced left and right children will be inserted - /// leftChildInsert - the rebalanced left child being inserted - /// rightChildInsert - the rebalanced right child being inserted - public func splitArrayAndInsertTwo(children : [var ?T], rebalancedChildIndex : Nat, leftChildInsert : T, rightChildInsert : T) : ([var ?T], [var ?T]) { - let splitIndex = children.size() / 2; - - let leftRebalancedChildren = VarArray.tabulate( - children.size(), - func(i) { - // only insert elements up to the split index and fill the rest of the children with nulls - if (i <= splitIndex) { - if (i < rebalancedChildIndex) { children[i] } - // insert the left and right rebalanced child halves if the rebalancedChildIndex comes before the splitIndex - else if (i == rebalancedChildIndex) { - ?leftChildInsert - } else if (i == rebalancedChildIndex + 1) { ?rightChildInsert } else { - children[i - 1] - } // i > rebalancedChildIndex - } else { null } - } - ); - - let rightRebalanceChildren : [var ?T] = - // Case 1: if both left and right rebalanced halves were inserted into the left child can just go from the split index onwards - if (rebalancedChildIndex + 1 <= splitIndex) { - VarArray.tabulate( - children.size(), - func(i) { - let adjIndex = i + splitIndex; - if (adjIndex < children.size()) { children[adjIndex] } else { null } - } - ) - } - // Case 2: if both left and right rebalanced halves will be inserted into the right child - else if (rebalancedChildIndex > splitIndex) { - var rebalanceOffset = 0; - VarArray.tabulate( - children.size(), - func(i) { - let adjIndex = i + splitIndex + 1; - if (adjIndex == rebalancedChildIndex) { ?leftChildInsert } else if (adjIndex == rebalancedChildIndex + 1) { - rebalanceOffset := 1; // after inserting both rebalanced children, any elements coming after are from the previous index - ?rightChildInsert - } else if (adjIndex <= children.size()) { - children[adjIndex - rebalanceOffset] - } else { null } - } - ) - } - // Case 3: if left rebalanced half was in left child, and right rebalanced half will be in right child - // rebalancedChildIndex == splitIndex - else { - VarArray.tabulate( - children.size(), - func(i) { - // first element is the right rebalanced half - if (i == 0) { ?rightChildInsert } else { - let adjIndex = i + splitIndex; - if (adjIndex < children.size()) { children[adjIndex] } else { - null - } - } - } - ) - }; - - (leftRebalancedChildren, rightRebalanceChildren) - }; - - /// Specific to the BTree delete implementation (assumes node ordering such that nulls come at the end of the array) - /// - /// Assumptions: - /// * All nulls come at the end of the array - /// * Assumes the delete index provided is correct and non null - will trap otherwise - /// * deleteIndex < array.size() - /// - /// Deletes an element from the the array, and then shifts all non-null elements coming after that deleted element by 1 - /// to the left. Returns the element that was deleted. - public func deleteAndShift(array : [var ?T], deleteIndex : Nat) : T { - var deleted : T = switch (array[deleteIndex]) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: deleteAndShift, an invalid/incorrect delete index was passed") - }; - case (?el) { el } - }; - - array[deleteIndex] := null; - - var i = deleteIndex + 1; - label l loop { - if (i >= array.size()) { break l }; - - switch (array[i]) { - case null { break l }; - case (?_) { - array[i - 1] := array[i] - } - }; - - i += 1 - }; - - array[i - 1] := null; - - deleted - }; - - // replaces two successive elements in the array with a single element and shifts all other elements to the left by 1 - public func replaceTwoWithElementAndShift(array : [var ?T], element : T, replaceIndex : Nat) { - array[replaceIndex] := ?element; - - var i = replaceIndex + 1; - let endShiftIndex : Nat = array.size() - 1; - while (i < endShiftIndex) { - switch (array[i]) { - case (?_) { array[i] := array[i + 1] }; - case null { return } - }; - - i += 1 - }; - - array[endShiftIndex] := null - }; - - /// BTree specific implementation - /// - /// In a single iteration insert at one position of the array while deleting at another position of the array, shifting all - /// elements as appropriate - /// - /// This is used when borrowing an element from an inorder predecessor/successor through the parent node - public func insertAtPostionAndDeleteAtPosition(array : [var ?T], insertElement : ?T, insertIndex : Nat, deleteIndex : Nat) : T { - var deleted : T = switch (array[deleteIndex]) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: insertAtPositionAndDeleteAtPosition, and incorrect delete index was passed") - }; // indicated an incorrect delete index was passed - trap - case (?el) { el } - }; - - // Example of this case: - // - // Insert Delete - // V V - //[var ?10, ?20, ?30, ?40, ?50] - if (insertIndex < deleteIndex) { - var i = deleteIndex; - while (i > insertIndex) { - array[i] := array[i - 1]; - i -= 1 - }; - - array[insertIndex] := insertElement - } - // Example of this case: - // - // Delete Insert - // V V - //[var ?10, ?20, ?30, ?40, ?50] - else if (insertIndex > deleteIndex) { - array[deleteIndex] := null; - var i = deleteIndex + 1; - label l loop { - if (i >= array.size()) { assert false; break l }; // TODO: remove? this should not happen since the insertIndex should get hit first? - - if (i == insertIndex) { - array[i - 1] := array[i]; - array[i] := insertElement; - break l - } else { - array[i - 1] := array[i] - }; - - i += 1 - }; - - } - // insertIndex == deleteIndex, can just do a swap - else { array[deleteIndex] := insertElement }; - - deleted - }; - - // which child the deletionIndex is referring to - public type DeletionSide = { #left; #right }; - - // merges a middle (parent) element with the left and right child arrays while deleting the element from the correct child by the deleteIndex passed - public func mergeParentWithChildrenAndDelete( - parentElement : ?T, - childCount : Nat, - leftChild : [var ?T], - rightChild : [var ?T], - deleteIndex : Nat, - deletionSide : DeletionSide - ) : ([var ?T], T) { - let mergedArray = VarArray.repeat(null, leftChild.size()); - var i = 0; - switch (deletionSide) { - case (#left) { - // BTree implementation expects the deleted element to exist - if null, traps - let deletedElement = switch (leftChild[deleteIndex]) { - case (?el) { el }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: mergeParentWithChildrenAndDelete, an invalid delete index was passed") - } - }; - - // copy over left child until deleted element is hit, then copy all elements after the deleted element - while (i < childCount) { - if (i < deleteIndex) { - mergedArray[i] := leftChild[i] - } else { - mergedArray[i] := leftChild[i + 1] - }; - i += 1 - }; - - // insert parent kv in the middle - mergedArray[childCount - 1] := parentElement; - - // copy over the rest of the right child elements - while (i < childCount * 2) { - mergedArray[i] := rightChild[i - childCount]; - i += 1 - }; - - (mergedArray, deletedElement) - }; - case (#right) { - // BTree implementation expects the deleted element to exist - if null, traps - let deletedElement = switch (rightChild[deleteIndex]) { - case (?el) { el }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: mergeParentWithChildrenAndDelete: element at deleted index must exist") - } - }; - // since deletion side is #right, can safely copy over all elements from the left child - while (i < childCount) { - mergedArray[i] := leftChild[i]; - i += 1 - }; - - // insert parent kv in the middle - mergedArray[childCount] := parentElement; - i += 1; - - var j = 0; - // copy over right child until deleted element is hit, then copy elements after the deleted element - while (i < childCount * 2) { - if (j < deleteIndex) { - mergedArray[i] := rightChild[j] - } else { - mergedArray[i] := rightChild[j + 1] - }; - i += 1; - j += 1 - }; - - (mergedArray, deletedElement) - } - } - }; - -} diff --git a/.mops/core@2.3.1/src/internal/PRNG.mo b/.mops/core@2.3.1/src/internal/PRNG.mo deleted file mode 100644 index 8a59861..0000000 --- a/.mops/core@2.3.1/src/internal/PRNG.mo +++ /dev/null @@ -1,76 +0,0 @@ -/// Collection of pseudo-random number generators -/// -/// The algorithms deliver deterministic statistical randomness, -/// not cryptographic randomness. -/// -/// Algorithm 1: 128-bit Seiran PRNG -/// See: https://github.com/andanteyk/prng-seiran -/// -/// Algorithm 2: SFC64 and SFC32 (Chris Doty-Humphrey’s Small Fast Chaotic PRNG) -/// See: https://numpy.org/doc/stable/reference/random/bit_generators/sfc64.html -/// -/// Copyright: 2023 MR Research AG -/// Main author: react0r-com -/// Contributors: Timo Hanke (timohanke) -import Nat "../Nat"; - -module { - /// Constructs an SFC 64-bit generator. - /// The recommended constructor arguments are: 24, 11, 3. - /// - /// Example: - /// ```motoko - /// import PRNG "mo:core/internal/PRNG"; - /// - /// let rng = PRNG.SFC64(24, 11, 3); - /// ``` - /// For convenience, the function `SFC64a()` returns a generator constructed - /// with the recommended parameter set (24, 11, 3). - public class SFC64(p : Nat64, q : Nat64, r : Nat64) { - // state - var a : Nat64 = 0; - var b : Nat64 = 0; - var c : Nat64 = 0; - var d : Nat64 = 0; - - /// Initializes the PRNG state with a particular seed - /// - /// Example: - /// ```motoko - public func init(seed : Nat64) = init3(seed, seed, seed); - - /// Initializes the PRNG state with a hardcoded seed. - /// No argument is required. - /// - /// Example: - public func initPre() = init(0xcafef00dbeef5eed); - - /// Initializes the PRNG state with three state variables - /// - /// Example: - public func init3(seed1 : Nat64, seed2 : Nat64, seed3 : Nat64) { - a := seed1; - b := seed2; - c := seed3; - d := 1; - - for (_ in Nat.range(0, 11)) ignore next() - }; - - /// Returns one output and advances the PRNG's state - /// - /// Example: - public func next() : Nat64 { - let tmp = a +% b +% d; - a := b ^ (b >> q); - b := c +% (c << r); - c := (c <<> p) +% tmp; - d +%= 1; - tmp - } - }; - - /// SFC64a is the same as numpy. - /// See: [sfc64_next()](https:///github.com/numpy/numpy/blob/b6d372c25fab5033b828dd9de551eb0b7fa55800/numpy/random/src/sfc64/sfc64.h#L28) - public func sfc64a() : SFC64 { SFC64(24, 11, 3) } -} diff --git a/.mops/core@2.3.1/src/internal/SortHelper.mo b/.mops/core@2.3.1/src/internal/SortHelper.mo deleted file mode 100644 index 2222e23..0000000 --- a/.mops/core@2.3.1/src/internal/SortHelper.mo +++ /dev/null @@ -1,1270 +0,0 @@ -import Runtime "../Runtime"; -import Order "../Order"; -import Prim "mo:⛔"; - -module { - let nat = Prim.nat32ToNat; - - // Must have: len <= 8 - // Use dest = buffer when sorting in place - public func insertionSortSmall(buffer : [var T], dest : [var T], compare : (T, T) -> Order.Order, newFrom : Nat32, len : Nat32) { - debug assert len > 0; - switch (len) { - case (1) { - let index0 = nat(newFrom); - dest[index0] := buffer[index0] - }; - case (2) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let t0 = buffer[index0]; - let t1 = buffer[index1]; - switch (compare(t1, t0)) { - case (#less) { - dest[index0] := t1; - dest[index1] := t0 - }; - case (_) { - dest[index0] := t0; - dest[index1] := t1 - } - } - }; - case (3) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - let t2 = buffer[index2]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - - switch (compare(t2, t1)) { - case (#less) { - switch (compare(t2, t0)) { - case (#less) { - dest[index0] := t2; - dest[index1] := t0; - dest[index2] := t1 - }; - case (_) { - dest[index0] := t0; - dest[index1] := t2; - dest[index2] := t1 - } - } - }; - case (_) { - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2 - } - } - }; - case (4) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - - switch (compare(t3, t2)) { - case (#less) { - tv := t3; - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3 - }; - case (5) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - let index4 = nat(newFrom +% 4); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - var t4 = buffer[index4]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3; - dest[index4] := t4 - }; - case (6) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - let index4 = nat(newFrom +% 4); - let index5 = nat(newFrom +% 5); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - var t4 = buffer[index4]; - var t5 = buffer[index5]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3; - dest[index4] := t4; - dest[index5] := t5 - }; - case (7) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - let index4 = nat(newFrom +% 4); - let index5 = nat(newFrom +% 5); - let index6 = nat(newFrom +% 6); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - var t4 = buffer[index4]; - var t5 = buffer[index5]; - var t6 = buffer[index6]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - tv := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3; - dest[index4] := t4; - dest[index5] := t5; - dest[index6] := t6 - }; - case (8) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - let index4 = nat(newFrom +% 4); - let index5 = nat(newFrom +% 5); - let index6 = nat(newFrom +% 6); - let index7 = nat(newFrom +% 7); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - var t4 = buffer[index4]; - var t5 = buffer[index5]; - var t6 = buffer[index6]; - var t7 = buffer[index7]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - tv := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) {} - }; - tv := t7; - switch (compare(tv, t6)) { - case (#less) { - t7 := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) { t6 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3; - dest[index4] := t4; - dest[index5] := t5; - dest[index6] := t6; - dest[index7] := t7 - }; - case (_) Runtime.trap("insertionSortSmall for len > 8 is not implemented.") - } - }; - - // sort from buffer to dest array at the given offset - public func insertionSortSmallMove(buffer : [var T], dest : [var T], compare : (T, T) -> Order.Order, newFrom : Nat32, len : Nat32, offset : Nat32) { - debug assert len > 0; - switch (len) { - case (1) { - dest[nat(offset)] := buffer[nat(newFrom)] - }; - case (2) { - let t0 = buffer[nat(newFrom)]; - let t1 = buffer[nat(newFrom +% 1)]; - switch (compare(t1, t0)) { - case (#less) { - dest[nat(offset)] := t1; - dest[nat(offset +% 1)] := t0 - }; - case (_) { - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1 - } - } - }; - case (3) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - let t2 = buffer[nat(newFrom +% 2)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - - switch (compare(t2, t1)) { - case (#less) { - switch (compare(t2, t0)) { - case (#less) { - dest[nat(offset)] := t2; - dest[nat(offset +% 1)] := t0; - dest[nat(offset +% 2)] := t1 - }; - case (_) { - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t2; - dest[nat(offset +% 2)] := t1 - } - } - }; - case (_) { - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2 - } - } - }; - case (4) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - - switch (compare(t3, t2)) { - case (#less) { - tv := t3; - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3 - }; - case (5) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - var t4 = buffer[nat(newFrom +% 4)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3; - dest[nat(offset +% 4)] := t4 - }; - case (6) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - var t4 = buffer[nat(newFrom +% 4)]; - var t5 = buffer[nat(newFrom +% 5)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3; - dest[nat(offset +% 4)] := t4; - dest[nat(offset +% 5)] := t5 - }; - case (7) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - var t4 = buffer[nat(newFrom +% 4)]; - var t5 = buffer[nat(newFrom +% 5)]; - var t6 = buffer[nat(newFrom +% 6)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - tv := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3; - dest[nat(offset +% 4)] := t4; - dest[nat(offset +% 5)] := t5; - dest[nat(offset +% 6)] := t6 - }; - case (8) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - var t4 = buffer[nat(newFrom +% 4)]; - var t5 = buffer[nat(newFrom +% 5)]; - var t6 = buffer[nat(newFrom +% 6)]; - var t7 = buffer[nat(newFrom +% 7)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - tv := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) {} - }; - tv := t7; - switch (compare(tv, t6)) { - case (#less) { - t7 := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) { t6 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3; - dest[nat(offset +% 4)] := t4; - dest[nat(offset +% 5)] := t5; - dest[nat(offset +% 6)] := t6; - dest[nat(offset +% 7)] := t7 - }; - case (_) Runtime.trap("insertionSortSmall for len > 8 is not implemented.") - } - } -} diff --git a/.mops/core@2.3.1/src/pure/List.mo b/.mops/core@2.3.1/src/pure/List.mo deleted file mode 100644 index c0d36f1..0000000 --- a/.mops/core@2.3.1/src/pure/List.mo +++ /dev/null @@ -1,1114 +0,0 @@ -/// Purely-functional, singly-linked list data structure. -/// This module provides immutable lists with efficient prepend and traversal operations. -/// -/// A list of type `List` is either `null` or an optional pair of a value of type `T` and a tail, itself of type `List`. -/// -/// To use this library, import it using: -/// -/// ```motoko name=import -/// import List "mo:core/pure/List"; -/// ``` - -import { Array_tabulate } "mo:⛔"; -import Array "../Array"; -import Iter "../Iter"; -import Order "../Order"; -import Result "../Result"; -import { trap } "../Runtime"; -import Types "../Types"; -import Runtime "../Runtime"; - -module { - - /// @deprecated M0235 - public type List = Types.Pure.List; - - /// Create an empty list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// assert List.empty() == null; - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func empty() : List = null; - - /// Check whether a list is empty and return true if the list is empty. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// assert List.isEmpty(null); - /// assert not List.isEmpty(?(1, null)); - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func isEmpty(self : List) : Bool = switch self { - case null true; - case _ false - }; - - /// Return the length of the list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, null)); - /// assert List.size(list) == 2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func size(self : List) : Nat = ( - func go(n : Nat, list : List) : Nat = switch list { - case (?(_, t)) go(n + 1, t); - case null n - } - )(0, self); - - /// Check whether the list contains a given value. Uses the provided equality function to compare values. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.contains(list, Nat.equal, 2); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func contains(self : List, equal : (implicit : (T, T) -> Bool), item : T) : Bool = switch self { - case (?(h, t)) equal(h, item) or contains(t, equal, item); - case _ false - }; - - /// Access any item in a list, zero-based. - /// - /// NOTE: Indexing into a list is a linear operation, and usually an - /// indication that a list might not be the best data structure - /// to use. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, null)); - /// assert List.get(list, 1) == ?1; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func get(self : List, n : Nat) : ?T = switch self { - case (?(h, t)) if (n == 0) ?h else get(t, n - 1 : Nat); - case null null - }; - - /// Add `item` to the head of `list`, and return the new list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// assert List.pushFront(null, 0) == ?(0, null); - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func pushFront(self : List, item : T) : List = ?(item, self); - - /// Return the last element of the list, if present. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, null)); - /// assert List.last(list) == ?1; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func last(self : List) : ?T = switch self { - case (?(h, null)) ?h; - case null null; - case (?(_, t)) last t - }; - - /// Remove the head of the list, returning the optioned head and the tail of the list in a pair. - /// Returns `(null, null)` if the list is empty. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, null)); - /// assert List.popFront(list) == (?0, ?(1, null)); - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func popFront(self : List) : (?T, List) = switch self { - case null (null, null); - case (?(h, t)) (?h, t) - }; - - /// Reverses the list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.reverse(list) == ?(2, ?(1, ?(0, null))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func reverse(self : List) : List = ( - func go(acc : List, list : List) : List = switch list { - case (?(h, t)) go(?(h, acc), t); - case null acc - } - )(null, self); - - /// Call the given function for its side effect, with each list element in turn. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// var sum = 0; - /// List.forEach(list, func n = sum += n); - /// assert sum == 3; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEach(self : List, f : T -> ()) = switch self { - case (?(h, t)) { f h; forEach(t, f) }; - case null () - }; - - /// Call the given function `f` on each list element and collect the results - /// in a new list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.map(list, Nat.toText) == ?("0", ?("1", ?("2", null))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func map(self : List, f : T1 -> T2) : List = ( - func go(list : List, f : T1 -> T2, acc : List) : List = switch list { - case (?(h, t)) go(t, f, ?(f h, acc)); - case null reverse acc - } - )(self, f, null); - - /// Create a new list with only those elements of the original list for which - /// the given function (often called the _predicate_) returns true. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.filter(list, func n = n != 1) == ?(0, ?(2, null)); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func filter(self : List, f : T -> Bool) : List = ( - func go(list : List, f : T -> Bool, acc : List) : List = switch list { - case (?(h, t)) if (f h) go(t, f, ?(h, acc)) else go(t, f, acc); - case null reverse acc - } - )(self, f, null); - - /// Call the given function on each list element, and collect the non-null results - /// in a new list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.filterMap( - /// list, - /// func n = if (n > 1) ?(n * 2) else null - /// ) == ?(4, ?(6, null)); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func filterMap(self : List, f : T -> ?R) : List = ( - func go(list : List, f : T -> ?R, acc : List) : List = switch list { - case (?(h, t)) switch (f h) { - case null go(t, f, acc); - case (?r) go(t, f, ?(r, acc)) - }; - case null reverse acc - } - )(self, f, null); - - /// Maps a `Result`-returning function `f` over a `List` and returns either - /// the first error or a list of successful values. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.mapResult( - /// list, - /// func n = if (n > 0) #ok(n * 2) else #err "Some element is zero" - /// ) == #ok(?(2, ?(4, ?(6, null)))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapResult(self : List, f : T -> Result.Result) : Result.Result, E> = ( - func rev(acc : List, list : List, f : T -> Result.Result) : Result.Result, E> = switch list { - case (?(h, t)) switch (f h) { - case (#ok fh) rev(?(fh, acc), t, f); - case (#err e) #err e - }; - case null #ok(reverse acc) - } - )(null, self, f); - - /// Create two new lists from the results of a given function (`f`). - /// The first list only includes the elements for which the given - /// function `f` returns true and the second list only includes - /// the elements for which the function returns false. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.partition(list, func n = n != 1) == (?(0, ?(2, null)), ?(1, null)); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func partition(self : List, f : T -> Bool) : (List, List) = ( - func go(list : List, f : T -> Bool, acc1 : List, acc2 : List) : (List, List) = switch list { - case (?(h, t)) if (f h) go(t, f, ?(h, acc1), acc2) else go(t, f, acc1, ?(h, acc2)); - case null (reverse acc1, reverse acc2) - } - )(self, f, null, null); - - /// Append the elements from one list to another list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list1 = ?(0, ?(1, ?(2, null))); - /// let list2 = ?(3, ?(4, ?(5, null))); - /// assert List.concat(list1, list2) == ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))); - /// } - /// ``` - /// - /// Runtime: O(size(l)) - /// - /// Space: O(size(l)) - public func concat(self : List, other : List) : List = revAppend(reverse self, other); - - /// Flatten, or repatedly concatenate, an iterator of lists as a list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let lists = [ ?(0, ?(1, ?(2, null))), - /// ?(3, ?(4, ?(5, null))) ]; - /// assert List.join(lists |> Iter.fromArray(_)) == ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))); - /// } - /// ``` - /// - /// Runtime: O(size*size) - /// - /// Space: O(size*size) - public func join(iter : Iter.Iter>) : List { - var acc : List = null; - for (list in iter) { - acc := revAppend(list, acc) - }; - reverse acc - }; - - /// Flatten, or repatedly concatenate, a list of lists as a list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let lists = ?(?(0, ?(1, ?(2, null))), - /// ?(?(3, ?(4, ?(5, null))), - /// null)); - /// assert List.flatten(lists) == ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))); - /// } - /// ``` - /// - /// Runtime: O(size*size) - /// - /// Space: O(size*size) - public func flatten(self : List>) : List = ( - func go(lists : List>, acc : List) : List = switch lists { - case (?(list, t)) go(t, revAppend(list, acc)); - case null reverse acc - } - )(self, null); - - /// Returns the first `n` elements of the given list. - /// If the given list has fewer than `n` elements, this function returns - /// a copy of the full input list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.take(list, 2) == ?(0, ?(1, null)); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - public func take(self : List, n : Nat) : List = ( - func go(n : Nat, list : List, acc : List) : List = if (n == 0) reverse acc else switch list { - case (?(h, t)) go(n - 1 : Nat, t, ?(h, acc)); - case null reverse acc - } - )(n, self, null); - - /// Drop the first `n` elements from the given list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.drop(list, 2) == ?(2, null); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(1) - public func drop(self : List, n : Nat) : List = if (n == 0) self else switch self { - case (?(_, t)) drop(t, n - 1 : Nat); - case null null - }; - - /// Collapses the elements in `list` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.foldLeft( - /// list, - /// "", - /// func (acc, x) = acc # Nat.toText(x) - /// ) == "123"; - /// } - /// ``` - /// - /// Runtime: O(size(list)) - /// - /// Space: O(1) heap, O(1) stack - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(self : List, base : A, combine : (A, T) -> A) : A = switch self { - case null base; - case (?(h, t)) foldLeft(t, combine(base, h), combine) - }; - - /// Collapses the elements in `buffer` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.foldRight( - /// list, - /// "", - /// func (x, acc) = Nat.toText(x) # acc - /// ) == "123"; - /// } - /// ``` - /// - /// Runtime: O(size(list)) - /// - /// Space: O(1) heap, O(size(list)) stack - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldRight(self : List, base : A, combine : (T, A) -> A) : A = ( - func go(list : List, base : A, combine : (T, A) -> A) : A = switch list { - case null base; - case (?(h, t)) go(t, combine(h, base), combine) - } - )(reverse self, base, combine); - - /// Return the first element for which the given predicate `f` is true, - /// if such an element exists. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.find(list, func n = n > 1) == ?2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func find(self : List, f : T -> Bool) : ?T = switch self { - case null null; - case (?(h, t)) if (f h) ?h else find(t, f) - }; - - /// Return the first index for which the given predicate `f` is true. - /// If no element satisfies the predicate, returns null. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.fromArray(['A', 'B', 'C', 'D']); - /// let found = List.findIndex(list, func(x) { x == 'C' }); - /// assert found == ?2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func findIndex(self : List, f : T -> Bool) : ?Nat { - findIndex_(self, 0, f) - }; - - private func findIndex_(self : List, index : Nat, f : T -> Bool) : ?Nat = switch self { - case null null; - case (?(h, t)) if (f h) ?index else findIndex_(t, index + 1, f) - }; - - /// Return true if the given predicate `f` is true for all list - /// elements. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert not List.all(list, func n = n > 1); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func all(self : List, f : T -> Bool) : Bool = switch self { - case null true; - case (?(h, t)) f h and all(t, f) - }; - - /// Return true if there exists a list element for which - /// the given predicate `f` is true. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.any(list, func n = n > 1); - /// } - /// ``` - /// - /// Runtime: O(size(list)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func any(self : List, f : T -> Bool) : Bool = switch self { - case null false; - case (?(h, t)) f h or any(t, f) - }; - - /// Merge two ordered lists into a single ordered list. - /// This function requires both list to be ordered as specified - /// by the given relation `compare`. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list1 = ?(1, ?(2, ?(4, null))); - /// let list2 = ?(2, ?(4, ?(6, null))); - /// assert List.merge(list1, list2, Nat.compare) == ?(1, ?(2, ?(2, ?(4, ?(4, ?(6, null)))))); - /// } - /// ``` - /// - /// Runtime: O(size(l1) + size(l2)) - /// - /// Space: O(size(l1) + size(l2)) - /// - /// *Runtime and space assumes that `lessThanOrEqual` runs in O(1) time and space. - public func merge(self : List, other : List, compare : (implicit : (T, T) -> Order.Order)) : List = ( - func go(list1 : List, list2 : List, compare : (T, T) -> Order.Order, acc : List) : List = switch (list1, list2) { - case ((null, l) or (l, null)) reverse(revAppend(l, acc)); - case (?(h1, t1), ?(h2, t2)) switch (compare(h1, h2)) { - case (#less or #equal) go(t1, list2, compare, ?(h1, acc)); - case (#greater) go(list1, t2, compare, ?(h2, acc)) - } - } - )(self, other, compare, null); - - /// Check if two lists are equal using the given equality function to compare elements. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list1 = ?(1, ?(2, null)); - /// let list2 = ?(1, ?(2, null)); - /// assert List.equal(list1, list2, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equalItem` runs in O(1) time and space. - public func equal(self : List, other : List, equalItem : (implicit : (equal : (T, T) -> Bool))) : Bool = switch (self, other) { - case (null, null) true; - case (?(h1, t1), ?(h2, t2)) equalItem(h1, h2) and equal(t1, t2, equalItem); - case _ false - }; - - /// Compare two lists using lexicographic ordering specified by argument function `compareItem`. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list1 = ?(1, ?(2, null)); - /// let list2 = ?(3, ?(4, null)); - /// assert List.compare(list1, list2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: O(size(l1)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that argument `compare` runs in O(1) time and space. - public func compare(self : List, other : List, compareItem : (implicit : (compare : (T, T) -> Order.Order))) : Order.Order = switch (self, other) { - case (?(h1, t1), ?(h2, t2)) switch (compareItem(h1, h2)) { - case (#equal) compare(t1, t2, compareItem); - case o o - }; - case (null, null) #equal; - case (null, _) #less; - case _ #greater - }; - - /// Generate a list based on a length and a function that maps from - /// a list index to a list element. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.tabulate(3, func n = n * 2); - /// assert list == ?(0, ?(2, ?(4, null))); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func tabulate(n : Nat, f : Nat -> T) : List { - var i = 0; - var l : List = null; - while (i < n) { - l := ?(f i, l); - i += 1 - }; - reverse l - }; - - /// Create a list with exactly one element. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// assert List.singleton(0) == ?(0, null); - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func singleton(item : T) : List = ?(item, null); - - /// Create a list of the given length with the same value in each position. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.repeat('a', 3); - /// assert list == ?('a', ?('a', ?('a', null))); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - public func repeat(item : T, n : Nat) : List { - var res : List = null; - var i : Int = n; - while (i != 0) { - i -= 1; - res := ?(item, res) - }; - res - }; - - /// Create a list of pairs from a pair of lists. - /// - /// If the given lists have different lengths, then the created list will have a - /// length equal to the length of the smaller list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list1 = ?(0, ?(1, ?(2, null))); - /// let list2 = ?("0", ?("1", null)); - /// assert List.zip(list1, list2) == ?((0, "0"), ?((1, "1"), null)); - /// } - /// ``` - /// - /// Runtime: O(min(size(xs), size(ys))) - /// - /// Space: O(min(size(xs), size(ys))) - public func zip(self : List, other : List) : List<(T, U)> = zipWith(self, other, func(x, y) = (x, y)); - - /// Create a list in which elements are created by applying function `f` to each pair `(x, y)` of elements - /// occuring at the same position in list `xs` and list `ys`. - /// - /// If the given lists have different lengths, then the created list will have a - /// length equal to the length of the smaller list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// import Char "mo:core/Char"; - /// - /// persistent actor { - /// let list1 = ?(0, ?(1, ?(2, null))); - /// let list2 = ?('a', ?('b', null)); - /// assert List.zipWith( - /// list1, - /// list2, - /// func (n, c) = Nat.toText(n) # Char.toText(c) - /// ) == ?("0a", ?("1b", null)); - /// } - /// ``` - /// - /// Runtime: O(min(size(xs), size(ys))) - /// - /// Space: O(min(size(xs), size(ys))) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func zipWith(self : List, other : List, f : (T, U) -> V) : List = ( - func go(list1 : List, list2 : List, f : (T, U) -> V, acc : List) : List = switch (list1, list2) { - case ((null, _) or (_, null)) reverse acc; - case (?(h1, t1), ?(h2, t2)) go(t1, t2, f, ?(f(h1, h2), acc)) - } - )(self, other, f, null); - - /// Split the given list at the given zero-based index. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.split(list, 2) == (?(0, ?(1, null)), ?(2, null)); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - public func split(self : List, n : Nat) : (List, List) { - func go(n : Nat, list : List, acc : List) : (List, List) = if (n == 0) (reverse acc, list) else switch list { - case (?(h, t)) go(n - 1 : Nat, t, ?(h, acc)); - case null (reverse acc, null) - }; - go(n, self, null) - }; - - /// Split the given list into chunks of length `n`. - /// The last chunk will be shorter if the length of the given list - /// does not divide by `n` evenly. Traps if `n` = 0. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// assert List.chunks(list, 2) == ?(?(0, ?(1, null)), ?(?(2, ?(3, null)), ?(?(4, null), null))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func chunks(self : List, n : Nat) : List> { - if (n == 0) trap "pure/List.chunks()"; - func go(list : List, n : Nat, acc : List>) : List> = switch (split(list, n)) { - case (null, _) reverse acc; - case (pre, null) reverse(?(pre, acc)); - case (pre, post) go(post, n, ?(pre, acc)) - }; - go(self, n, null) - }; - - /// Returns an iterator to the elements in the list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = List.fromArray([3, 1, 4]); - /// var text = ""; - /// for (item in List.values(list)) { - /// text #= Nat.toText(item); - /// }; - /// assert text == "314"; - /// } - /// ``` - public func values(self : List) : Iter.Iter = object { - var l = self; - public func next() : ?T = switch l { - case null null; - case (?(h, t)) { - l := t; - ?h - } - } - }; - - /// Returns an iterator to the `(index, element)` pairs in the list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = List.fromArray([3, 1, 4]); - /// var text = ""; - /// for ((index, element) in List.enumerate(list)) { - /// text #= Nat.toText(index); - /// }; - /// assert text == "012"; - /// } - /// ``` - public func enumerate(self : List) : Iter.Iter<(Nat, T)> = object { - var i = 0; - var l = self; - public func next() : ?(Nat, T) = switch l { - case null null; - case (?(h, t)) { - l := t; - let index = i; - i += 1; - ?(index, h) - } - } - }; - - /// Convert an array into a list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.fromArray([0, 1, 2, 3, 4]); - /// assert list == ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromArray(array : [T]) : List { - func go(from : Nat) : List = if (from < array.size()) ?(array.get from, go(from + 1)) else null; - go 0 - }; - - /// Convert a mutable array into a list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.fromVarArray([var 0, 1, 2, 3, 4]); - /// assert list == ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromVarArray(array : [var T]) : List = fromArray(Array.fromVarArray(array)); - - /// Create an array from a list. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Array "mo:core/Array"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let array = List.toArray(?(0, ?(1, ?(2, ?(3, ?(4, null)))))); - /// assert Array.equal(array, [0, 1, 2, 3, 4], Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toArray(self : List) : [T] { - var l = self; - Array_tabulate(size self, func _ { let ?(h, t) = l else Runtime.trap("List.toArray(): unreachable"); l := t; h }) - }; - - /// Create a mutable array from a list. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Array "mo:core/Array"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let array = List.toVarArray(?(0, ?(1, ?(2, ?(3, ?(4, null)))))); - /// assert Array.equal(Array.fromVarArray(array), [0, 1, 2, 3, 4], Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toVarArray(self : List) : [var T] = Array.toVarArray(toArray(self)); - - /// Create a list from an iterator, consuming the iterator. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.fromIter([0, 1, 2, 3, 4].vals()); - /// assert list == ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromIter(iter : Iter.Iter) : List { - var result : List = null; - for (x in iter) { - result := ?(x, result) - }; - reverse result - }; - - /// Convert an iterator to a list, consuming the iterator. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// transient let iter = [0, 1, 2, 3, 4].vals(); - /// - /// let list = iter.toList(); - /// - /// assert list == ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toList(self : Iter.Iter) : List { - fromIter(self) - }; - - /// Convert a list to a text representation using the provided function to convert each element to text. - /// The resulting text will be in the format "[element1, element2, ...]". - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.toText(list, Nat.toText) == "PureList[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toText(self : List, f : (implicit : T -> Text)) : Text { - var text = "PureList["; - var first = true; - forEach( - self, - func(item : T) { - if first { - first := false - } else { - text #= ", " - }; - text #= f item - } - ); - text # "]" - }; - - // revAppend([x1 .. xn], [y1 .. ym]) = [xn .. x1, y1 .. ym] - func revAppend(l : List, m : List) : List = switch l { - case (?(h, t)) revAppend(t, ?(h, m)); - case null m - } -} diff --git a/.mops/core@2.3.1/src/pure/Map.mo b/.mops/core@2.3.1/src/pure/Map.mo deleted file mode 100644 index ebddab3..0000000 --- a/.mops/core@2.3.1/src/pure/Map.mo +++ /dev/null @@ -1,1563 +0,0 @@ -/// Immutable, ordered key-value maps. -/// -/// The map type is stable whenever the key and value types are stable, allowing -/// map values to be stored in stable variables. -/// -/// Keys are ordered by an explicit `compare` function, which *must* be the same -/// across all operations on a given map. -/// -/// -/// Example: -/// ```motoko -/// import Map "mo:core/pure/Map"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// // creation -/// let empty = Map.empty(); -/// // insertion -/// let map1 = Map.add(empty, Nat.compare, 0, "Zero"); -/// // retrieval -/// assert Map.get(empty, Nat.compare, 0) == null; -/// assert Map.get(map1, Nat.compare, 0) == ?"Zero"; -/// // removal -/// let map2 = Map.remove(map1, Nat.compare, 0); -/// assert not Map.isEmpty(map1); -/// assert Map.isEmpty(map2); -/// } -/// ``` -/// -/// The internal representation is a red-black tree. -/// -/// A red-black tree is a balanced binary search tree ordered by the keys. -/// -/// The tree data structure internally colors each of its nodes either red or black, -/// and uses this information to balance the tree during the modifying operations. -/// -/// Performance: -/// * Runtime: `O(log(n))` worst case cost per insertion, removal, and retrieval operation. -/// * Space: `O(n)` for storing the entire tree. -/// `n` denotes the number of key-value entries (i.e. nodes) stored in the tree. -/// -/// Note: -/// * Map operations, such as retrieval, insertion, and removal create `O(log(n))` temporary objects that become garbage. -/// -/// Credits: -/// -/// The core of this implementation is derived from: -/// -/// * Ken Friis Larsen's [RedBlackMap.sml](https://github.com/kfl/mosml/blob/master/src/mosmllib/Redblackmap.sml), which itself is based on: -/// * Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](http://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html). - -import Order "../Order"; -import Iter "../Iter"; -import Types "../Types"; -import Runtime "../Runtime"; - -// TODO: inline Internal? -// TODO: Do we want clone or clear, just to match imperative API? -// inline Tree type, remove Types.Pure.Tree? - -module { - - /// @deprecated M0235 - public type Map = Types.Pure.Map; - - type Tree = Types.Pure.Map.Tree; - - /// Create a new empty immutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// assert Map.size(map) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Map { - Internal.empty() - }; - - /// Determines whether a key-value map is empty. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map0 = Map.empty(); - /// let map1 = Map.add(map0, Nat.compare, 0, "Zero"); - /// - /// assert Map.isEmpty(map0); - /// assert not Map.isEmpty(map1); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func isEmpty(self : Map) : Bool { - self.size == 0 - }; - - /// Determine the size of the map as the number of key-value entries. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.size(map) == 3; - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func size(self : Map) : Nat = self.size; - - /// Test whether the map `map`, ordered by `compare`, contains a binding for the given `key`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.containsKey(map, Nat.compare, 1); - /// assert not Map.containsKey(map, Nat.compare, 42); - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func containsKey(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : Bool = Internal.contains(self.root, compare, key); - - /// Given, `map` ordered by `compare`, return the value associated with key `key` if present and `null` otherwise. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.get(map, Nat.compare, 1) == ?"One"; - /// assert Map.get(map, Nat.compare, 42) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func get(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : ?V = Internal.get(self.root, compare, key); - - /// Given `map` ordered by `compare`, insert a mapping from `key` to `value`. - /// Returns the modified map and `true` if the key is new to map, otherwise `false`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = Map.empty(); - /// - /// do { - /// let (map1, new1) = Map.insert(map0, Nat.compare, 0, "Zero"); - /// assert Iter.toArray(Map.entries(map1)) == [(0, "Zero")]; - /// assert new1; - /// let (map2, new2) = Map.insert(map1, Nat.compare, 0, "Nil"); - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Nil")]; - /// assert not new2 - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `m := Map.add(m, cmp, k, v)`) - /// causes collecting `O(log(n))` nodes. - public func insert(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : (Map, Bool) { - switch (swap(self, compare, key, value)) { - case (map1, null) (map1, true); - case (map1, _) (map1, false) - } - }; - - /// Given `map` ordered by `compare`, add a new mapping from `key` to `value`. - /// Replaces any existing entry with key `key`. - /// Returns the modified map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// var map = Map.empty(); - /// - /// map := Map.add(map, Nat.compare, 0, "Zero"); - /// map := Map.add(map, Nat.compare, 1, "One"); - /// map := Map.add(map, Nat.compare, 0, "Nil"); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Nil"), (1, "One")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `m := Map.add(m, cmp, k, v)`) - /// causes collecting `O(log(n))` nodes. - public func add(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : Map { - swap(self, compare, key, value).0 - }; - - /// Given `map` ordered by `compare`, add a mapping from `key` to `value`. Overwrites any existing entry with key `key`. - /// Returns the modified map and the previous value associated with key `key` - /// or `null` if no such value exists. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// do { - /// let (map1, old1) = Map.swap(map0, Nat.compare, 0, "Nil"); - /// assert Iter.toArray(Map.entries(map1)) == [(0, "Nil"), (1, "One"), (2, "Two")]; - /// assert old1 == ?"Zero"; - /// - /// let (map2, old2) = Map.swap(map0, Nat.compare, 3, "Three"); - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Zero"), (1, "One"), (2, "Two"), (3, "Three")]; - /// assert old2 == null; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `m := Map.swap(m, Nat.compare, k, v).0`) - /// causes collecting `O(log(n))` nodes. - public func swap(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : (Map, ?V) { - switch (Internal.swap(self.root, compare, key, value)) { - case (t, null) { ({ root = t; size = self.size + 1 }, null) }; - case (t, v) { ({ root = t; size = self.size }, v) } - } - }; - - /// Overwrites the value of an existing key and returns the updated map and previous value. - /// If the key does not exist, returns the original map and `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let singleton = Map.singleton(0, "Zero"); - /// - /// do { - /// let (map1, prev1) = Map.replace(singleton, Nat.compare, 0, "Nil"); // overwrites the value for existing key. - /// assert prev1 == ?"Zero"; - /// assert Map.get(map1, Nat.compare, 0) == ?"Nil"; - /// - /// let (map2, prev2) = Map.replace(map1, Nat.compare, 1, "One"); // no effect, key is absent - /// assert prev2 == null; - /// assert Map.get(map2, Nat.compare, 1) == null; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func replace(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : (Map, ?V) { - // TODO: Could be optimized in future - if (containsKey(self, compare, key)) { - swap(self, compare, key, value) - } else { (self, null) } - }; - - /// Given a `map`, ordered by `compare`, deletes any entry for `key` from `map`. - /// Has no effect if `key` is not present in the map. - /// Returns the updated map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = - /// Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// let map1 = Map.remove(map0, Nat.compare, 1); - /// assert Iter.toArray(Map.entries(map1)) == [(0, "Zero"), (2, "Two")]; - /// let map2 = Map.remove(map0, Nat.compare, 42); - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `map := Map.delete(map, compare, k).0`) - /// causes collecting `O(log(n))` nodes. - public func remove(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : Map { - switch (Internal.remove(self.root, compare, key)) { - case (_, null) self; - case (t, ?_) { { root = t; size = self.size - 1 } } - } - }; - - /// Given a `map`, ordered by `compare`, deletes any entry for `key` from `map`. - /// Has no effect if `key` is not present in the map. - /// Returns the updated map and `true` if the `key` was present in `map`, otherwise `false`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = - /// Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// do { - /// let (map1, pres1) = Map.delete(map0, Nat.compare, 1); - /// assert Iter.toArray(Map.entries(map1)) == [(0, "Zero"), (2, "Two")]; - /// assert pres1; - /// let (map2, pres2) = Map.delete(map0, Nat.compare, 42); - /// assert not pres2; - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `map := Map.delete(map, compare, k).0`) - /// causes collecting `O(log(n))` nodes. - public func delete(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : (Map, Bool) { - switch (Internal.remove(self.root, compare, key)) { - case (_, null) { (self, false) }; - case (t, ?_) { ({ root = t; size = self.size - 1 }, true) } - } - }; - - /// Given a `map`, ordered by `compare`, deletes the entry for `key`. Returns a modified map, leaving `map` unchanged, and the - /// previous value associated with `key` or `null` if no such value exists. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// do { - /// let (map1, prev1) = Map.take(map0, Nat.compare, 0); - /// assert Iter.toArray(Map.entries(map1)) == [(1, "One"), (2, "Two")]; - /// assert prev1 == ?"Zero"; - /// - /// let (map2, prev2) = Map.take(map0, Nat.compare, 42); - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// assert prev2 == null; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `map := Map.remove(map, compare, key)`) - /// causes collecting `O(log(n))` nodes. - public func take(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : (Map, ?V) { - switch (Internal.remove(self.root, compare, key)) { - case (t, null) { ({ root = t; size = self.size }, null) }; - case (t, v) { ({ root = t; size = self.size - 1 }, v) } - } - }; - - /// Given a `map` retrieves the key-value pair in `map` with a maximal key. If `map` is empty returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.maxEntry(map) == ?(2, "Two"); - /// assert Map.maxEntry(Map.empty()) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func maxEntry(self : Map) : ?(K, V) = Internal.maxEntry(self.root); - - /// Retrieves a key-value pair from `map` with the minimal key. If the map is empty returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.minEntry(map) == ?(0, "Zero"); - /// assert Map.minEntry(Map.empty()) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func minEntry(self : Map) : ?(K, V) = Internal.minEntry(self.root); - - /// Returns an Iterator (`Iter`) over the key-value pairs in the map. - /// Iterator provides a single method `next()`, which returns - /// pairs in ascending order by keys, or `null` when out of pairs to iterate over. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// var sum = 0; - /// var text = ""; - /// for ((k, v) in Map.entries(map)) { sum += k; text #= v }; - /// assert sum == 3; - /// assert text == "ZeroOneTwo" - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func entries(self : Map) : Iter.Iter<(K, V)> = Internal.iter(self.root, #fwd); - - /// Returns an Iterator (`Iter`) over the key-value pairs in the map. - /// Iterator provides a single method `next()`, which returns - /// pairs in descending order by keys, or `null` when out of pairs to iterate over. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.reverseEntries(map)) == [(2, "Two"), (1, "One"), (0, "Zero")]; - /// var sum = 0; - /// var text = ""; - /// for ((k, v) in Map.reverseEntries(map)) { sum += k; text #= v }; - /// assert sum == 3; - /// assert text == "TwoOneZero" - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func reverseEntries(self : Map) : Iter.Iter<(K, V)> = Internal.iter(self.root, #bwd); - - /// Given a `map`, returns an Iterator (`Iter`) over the keys of the `map`. - /// Iterator provides a single method `next()`, which returns - /// keys in ascending order, or `null` when out of keys to iterate over. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.keys(map)) == [0, 1, 2]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func keys(self : Map) : Iter.Iter = Iter.map(entries(self), func(kv : (K, V)) : K { kv.0 }); - - /// Given a `map`, returns an Iterator (`Iter`) over the values of the map. - /// Iterator provides a single method `next()`, which returns - /// values in ascending order of associated keys, or `null` when out of values to iterate over. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.values(map)) == ["Zero", "One", "Two"]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func values(self : Map) : Iter.Iter = Iter.map(entries(self), func(kv : (K, V)) : V { kv.1 }); - - /// Returns a new map, containing all entries given by the iterator `i`. - /// If there are multiple entries with the same key the last one is taken. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = - /// Iter.fromArray([(0, "Zero"), (2, "Two"), (1, "One")]); - /// - /// let map = Map.fromIter(iter, Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func fromIter(iter : Iter.Iter<(K, V)>, compare : (implicit : (K, K) -> Order.Order)) : Map = Internal.fromIter(iter, compare); - - /// Convert an iterator of entries into a map. - /// If there are multiple entries with the same key the last one is taken. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = - /// Iter.fromArray([(0, "Zero"), (2, "Two"), (1, "One")]); - /// - /// let map = iter.toMap(Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func toMap(self : Iter.Iter<(K, V)>, compare : (implicit : (K, K) -> Order.Order)) : Map = Internal.fromIter(self, compare); - - /// Given a `map` and function `f`, creates a new map by applying `f` to each entry in the map `m`. Each entry - /// `(k, v)` in the old map is transformed into a new entry `(k, v2)`, where - /// the new value `v2` is created by applying `f` to `(k, v)`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func f(key : Nat, _val : Text) : Nat = key * 2; - /// - /// let resMap = Map.map(map, f); - /// - /// assert Iter.toArray(Map.entries(resMap)) == [(0, 0), (1, 2), (2, 4)]; - /// } - /// ``` - /// - /// Cost of mapping all the elements: - /// Runtime: `O(n)`. - /// Space: `O(n)` retained memory - /// where `n` denotes the number of key-value entries stored in the map. - public func map(self : Map, f : (K, V1) -> V2) : Map = Internal.map(self, f); - - /// Collapses the elements in the `map` into a single value by starting with `base` - /// and progressively combining keys and values into `base` with `combine`. Iteration runs - /// left to right. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func folder(accum : (Nat, Text), key : Nat, val : Text) : ((Nat, Text)) - /// = (key + accum.0, accum.1 # val); - /// - /// assert Map.foldLeft(map, (0, ""), folder) == (3, "ZeroOneTwo"); - /// } - /// ``` - /// - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: depends on `combine` function plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func foldLeft( - self : Map, - base : A, - combine : (A, K, V) -> A - ) : A = Internal.foldLeft(self.root, base, combine); - - /// Collapses the elements in the `map` into a single value by starting with `base` - /// and progressively combining keys and values into `base` with `combine`. Iteration runs - /// right to left. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func folder(key : Nat, val : Text, accum : (Nat, Text)) : ((Nat, Text)) - /// = (key + accum.0, accum.1 # val); - /// - /// assert Map.foldRight(map, (0, ""), folder) == (3, "TwoOneZero"); - /// } - /// ``` - /// - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: depends on `combine` function plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func foldRight( - self : Map, - base : A, - combine : (K, V, A) -> A - ) : A = Internal.foldRight(self.root, base, combine); - - /// Test whether all key-value pairs in `map` satisfy the given predicate `pred`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "0"), (2, "2"), (1, "1")].values(), Nat.compare); - /// - /// assert Map.all(map, func (k, v) = v == Nat.toText(k)); - /// assert not Map.all(map, func (k, v) = k < 2); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func all(self : Map, pred : (K, V) -> Bool) : Bool = Internal.all(self.root, pred); - - /// Test if any key-value pair in `map` satisfies the given predicate `pred`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "0"), (2, "2"), (1, "1")].values(), Nat.compare); - /// - /// assert Map.any(map, func (k, v) = (k >= 0)); - /// assert not Map.any(map, func (k, v) = (k >= 3)); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func any(self : Map, pred : (K, V) -> Bool) : Bool = Internal.any(self.root, pred); - - /// Create a new immutable key-value `map` with a single entry. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.singleton(0, "Zero"); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero")]; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func singleton(key : K, value : V) : Map { - { - size = 1; - root = #red(#leaf, key, value, #leaf) - } - }; - - /// Apply an operation for each key-value pair contained in the map. - /// The operation is applied in ascending order of the keys. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// var sum = 0; - /// var text = ""; - /// Map.forEach(map, func (key, value) { - /// sum += key; - /// text #= value; - /// }); - /// assert sum == 3; - /// assert text == "ZeroOneTwo"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - public func forEach(self : Map, operation : (K, V) -> ()) = Internal.forEach(self, operation); - - /// Filter entries in a new map. - /// Returns a new map that only contains the key-value pairs - /// that fulfil the criterion function. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numberNames = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// let evenNames = Map.filter(numberNames, Nat.compare, func (key, value) { - /// key % 2 == 0 - /// }); - /// - /// assert Iter.toArray(Map.entries(evenNames)) == [(0, "Zero"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func filter(self : Map, compare : (implicit : (K, K) -> Order.Order), criterion : (K, V) -> Bool) : Map = Internal.filter(self, compare, criterion); - - /// Given a `map`, comparison `compare` and function `f`, - /// constructs a new map ordered by `compare`, by applying `f` to each entry in `map`. - /// For each entry `(k, v)` in the old map, if `f` evaluates to `null`, the entry is discarded. - /// Otherwise, the entry is transformed into a new entry `(k, v2)`, where - /// the new value `v2` is the result of applying `f` to `(k, v)`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func f(key : Nat, val : Text) : ?Text { - /// if(key == 0) {null} - /// else { ?("Twenty " # val)} - /// }; - /// - /// let newMap = Map.filterMap(map, Nat.compare, f); - /// - /// assert Iter.toArray(Map.entries(newMap)) == [(1, "Twenty One"), (2, "Twenty Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func filterMap(self : Map, compare : (implicit : (K, K) -> Order.Order), f : (K, V1) -> ?V2) : Map = Internal.mapFilter(self, compare : (K, K) -> Order.Order, f); - - /// Validate the representation invariants of the given `map`. - /// Assert if any invariants are violated. - public func assertValid(self : Map, compare : (implicit : (K, K) -> Order.Order)) : () = Internal.validate(self, compare); - - /// Converts the `map` to its textual representation using `keyFormat` and `valueFormat` to convert each key and value to `Text`. - /// - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// assert Map.toText(map, Nat.toText, func t { t }) == "PureMap{(0, Zero), (1, One), (2, Two)}"; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `keyFormat` and `valueFormat` run in O(1) time and space. - public func toText(self : Map, keyFormat : (implicit : (toText : K -> Text)), valueFormat : (implicit : (toText : V -> Text))) : Text { - var text = "PureMap{"; - var sep = ""; - for ((k, v) in entries(self)) { - text #= sep # "(" # keyFormat(k) # ", " # valueFormat(v) # ")"; - sep := ", " - }; - text # "}" - }; - - /// Test whether two immutable maps have equal entries. - /// Assumes both maps are ordered equivalently. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let map1 = Map.fromIter([(0, "Zero"), (1, "One"), (2, "Two")].values(), Nat.compare); - /// let map2 = Map.fromIter([(2, "Two"), (1, "One"), (0, "Zero")].values(), Nat.compare); - /// assert(Map.equal(map1, map2, Nat.compare, Text.equal)); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func equal(self : Map, other : Map, compare : (implicit : (K, K) -> Order.Order), equal : (implicit : (V, V) -> Bool)) : Bool { - if (self.size != other.size) { - return false - }; - let iterator1 = entries(self); - let iterator2 = entries(other); - loop { - let next1 = iterator1.next(); - let next2 = iterator2.next(); - switch (next1, next2) { - case (null, null) { - return true - }; - case (?(key1, value1), ?(key2, value2)) { - if (not (compare(key1, key2) == #equal) or not equal(value1, value2)) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Compare two maps by primarily comparing keys and secondarily values. - /// Both maps are iterated by the ascending order of their creation and - /// order is determined by the following rules: - /// Less: - /// `map1` is less than `map2` if: - /// * the pairwise iteration hits a entry pair `entry1` and `entry2` where - /// `entry1` is less than `entry2` and all preceding entry pairs are equal, or, - /// * `map1` is a strict prefix of `map2`, i.e. `map2` has more entries than `map1` - /// and all entries of `map1` occur at the beginning of iteration `map2`. - /// `entry1` is less than `entry2` if: - /// * the key of `entry1` is less than the key of `entry2`, or - /// * `entry1` and `entry2` have equal keys and the value of `entry1` is less than - /// the value of `entry2`. - /// Equal: - /// `map1` and `map2` have same series of equal entries by pairwise iteration. - /// Greater: - /// `map1` is neither less nor equal `map2`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let map1 = Map.fromIter([(0, "Zero"), (1, "One")].values(), Nat.compare); - /// let map2 = Map.fromIter([(0, "Zero"), (2, "Two")].values(), Nat.compare); - /// - /// assert Map.compare(map1, map2, Nat.compare, Text.compare) == #less; - /// assert Map.compare(map1, map1, Nat.compare, Text.compare) == #equal; - /// assert Map.compare(map2, map1, Nat.compare, Text.compare) == #greater - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that `compareKey` and `compareValue` have runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func compare(self : Map, other : Map, compareKey : (implicit : (compare : (K, K) -> Order.Order)), compareValue : (implicit : (compare : (V, V) -> Order.Order))) : Order.Order { - let iterator1 = entries(self); - let iterator2 = entries(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?(key1, value1), ?(key2, value2)) { - let keyComparison = compareKey(key1, key2); - if (keyComparison != #equal) { - return keyComparison - }; - let valueComparison = compareValue(value1, value2); - if (valueComparison != #equal) { - return valueComparison - } - } - } - } - }; - - module Internal { - - public func empty() : Map { { size = 0; root = #leaf } }; - - public func fromIter(i : Iter.Iter<(K, V)>, compare : (K, K) -> Order.Order) : Map { - var map = #leaf : Tree; - var size = 0; - for (val in i) { - map := add(map, compare, val.0, val.1); - size += 1 - }; - { root = map; size } - }; - - type List = Types.Pure.List; - - type IterRep = List<{ #tr : Tree; #xy : (K, V) }>; - - public func iter(map : Tree, direction : { #fwd; #bwd }) : Iter.Iter<(K, V)> { - let turnLeftFirst : MapTraverser = func(l, x, y, r, ts) { - ?(#tr(l), ?(#xy(x, y), ?(#tr(r), ts))) - }; - - let turnRightFirst : MapTraverser = func(l, x, y, r, ts) { - ?(#tr(r), ?(#xy(x, y), ?(#tr(l), ts))) - }; - - switch direction { - case (#fwd) IterMap(map, turnLeftFirst); - case (#bwd) IterMap(map, turnRightFirst) - } - }; - - type MapTraverser = (Tree, K, V, Tree, IterRep) -> IterRep; - - class IterMap(tree : Tree, mapTraverser : MapTraverser) { - var trees : IterRep = ?(#tr(tree), null); - public func next() : ?(K, V) { - switch (trees) { - case (null) { null }; - case (?(#tr(#leaf), ts)) { - trees := ts; - next() - }; - case (?(#xy(xy), ts)) { - trees := ts; - ?xy - }; - case (?(#tr(#red(l, x, y, r)), ts)) { - trees := mapTraverser(l, x, y, r, ts); - next() - }; - case (?(#tr(#black(l, x, y, r)), ts)) { - trees := mapTraverser(l, x, y, r, ts); - next() - } - } - } - }; - - public func map(map : Map, f : (K, V1) -> V2) : Map { - func mapRec(m : Tree) : Tree { - switch m { - case (#leaf) { #leaf }; - case (#red(l, x, y, r)) { - #red(mapRec l, x, f(x, y), mapRec r) - }; - case (#black(l, x, y, r)) { - #black(mapRec l, x, f(x, y), mapRec r) - } - } - }; - { size = map.size; root = mapRec(map.root) } - }; - - public func foldLeft( - map : Tree, - base : Accum, - combine : (Accum, Key, Value) -> Accum - ) : Accum { - switch (map) { - case (#leaf) { base }; - case (#red(l, k, v, r)) { - let left = foldLeft(l, base, combine); - let middle = combine(left, k, v); - foldLeft(r, middle, combine) - }; - case (#black(l, k, v, r)) { - let left = foldLeft(l, base, combine); - let middle = combine(left, k, v); - foldLeft(r, middle, combine) - } - } - }; - - public func foldRight( - map : Tree, - base : Accum, - combine : (Key, Value, Accum) -> Accum - ) : Accum { - switch (map) { - case (#leaf) { base }; - case (#red(l, k, v, r)) { - let right = foldRight(r, base, combine); - let middle = combine(k, v, right); - foldRight(l, middle, combine) - }; - case (#black(l, k, v, r)) { - let right = foldRight(r, base, combine); - let middle = combine(k, v, right); - foldRight(l, middle, combine) - } - } - }; - - public func forEach(map : Map, operation : (K, V) -> ()) { - func combine(_acc : Null, key : K, value : V) : Null { - operation(key, value); - null - }; - ignore foldLeft(map.root, null, combine) - }; - - public func filter(map : Map, compare : (K, K) -> Order.Order, criterion : (K, V) -> Bool) : Map { - var size = 0; - func combine(acc : Tree, key : K, value : V) : Tree { - if (criterion(key, value)) { - size += 1; - add(acc, compare, key, value) - } else acc - }; - { root = foldLeft(map.root, #leaf, combine); size } - }; - - public func mapFilter(map : Map, compare : (K, K) -> Order.Order, f : (K, V1) -> ?V2) : Map { - var size = 0; - func combine(acc : Tree, key : K, value1 : V1) : Tree { - switch (f(key, value1)) { - case null { acc }; - case (?value2) { - size += 1; - add(acc, compare, key, value2) - } - } - }; - { root = foldLeft(map.root, #leaf, combine); size } - }; - - public func get(t : Tree, compare : (K, K) -> Order.Order, x : K) : ?V { - switch t { - case (#red(l, x1, y1, r)) { - switch (compare(x, x1)) { - case (#less) { get(l, compare, x) }; - case (#equal) { ?y1 }; - case (#greater) { get(r, compare, x) } - } - }; - case (#black(l, x1, y1, r)) { - switch (compare(x, x1)) { - case (#less) { get(l, compare, x) }; - case (#equal) { ?y1 }; - case (#greater) { get(r, compare, x) } - } - }; - case (#leaf) { null } - } - }; - - public func contains(m : Tree, compare : (K, K) -> Order.Order, key : K) : Bool { - switch (get(m, compare, key)) { - case (null) { false }; - case (_) { true } - } - }; - - public func maxEntry(m : Tree) : ?(K, V) { - func rightmost(m : Tree) : (K, V) { - switch m { - case (#red(_, k, v, #leaf)) { (k, v) }; - case (#red(_, _, _, r)) { rightmost(r) }; - case (#black(_, k, v, #leaf)) { (k, v) }; - case (#black(_, _, _, r)) { rightmost(r) }; - case (#leaf) { Runtime.trap "pure/Map.maxEntry() impossible" } - } - }; - switch m { - case (#leaf) { null }; - case (_) { ?rightmost(m) } - } - }; - - public func minEntry(m : Tree) : ?(K, V) { - func leftmost(m : Tree) : (K, V) { - switch m { - case (#red(#leaf, k, v, _)) { (k, v) }; - case (#red(l, _, _, _)) { leftmost(l) }; - case (#black(#leaf, k, v, _)) { (k, v) }; - case (#black(l, _, _, _)) { leftmost(l) }; - case (#leaf) { Runtime.trap "pure/Map.minEntry() impossible" } - } - }; - switch m { - case (#leaf) { null }; - case (_) { ?leftmost(m) } - } - }; - - public func all(m : Tree, pred : (K, V) -> Bool) : Bool { - switch m { - case (#red(l, k, v, r)) { - pred(k, v) and all(l, pred) and all(r, pred) - }; - case (#black(l, k, v, r)) { - pred(k, v) and all(l, pred) and all(r, pred) - }; - case (#leaf) { true } - } - }; - - public func any(m : Tree, pred : (K, V) -> Bool) : Bool { - switch m { - case (#red(l, k, v, r)) { - pred(k, v) or any(l, pred) or any(r, pred) - }; - case (#black(l, k, v, r)) { - pred(k, v) or any(l, pred) or any(r, pred) - }; - case (#leaf) { false } - } - }; - - func redden(t : Tree) : Tree { - switch t { - case (#black(l, x, y, r)) { (#red(l, x, y, r)) }; - case _ { - Runtime.trap "pure/Map.redden() impossible" - } - } - }; - - func lbalance(left : Tree, x : K, y : V, right : Tree) : Tree { - switch (left, right) { - case (#red(#red(l1, x1, y1, r1), x2, y2, r2), r) { - #red( - #black(l1, x1, y1, r1), - x2, - y2, - #black(r2, x, y, r) - ) - }; - case (#red(l1, x1, y1, #red(l2, x2, y2, r2)), r) { - #red( - #black(l1, x1, y1, l2), - x2, - y2, - #black(r2, x, y, r) - ) - }; - case _ { - #black(left, x, y, right) - } - } - }; - - func rbalance(left : Tree, x : K, y : V, right : Tree) : Tree { - switch (left, right) { - case (l, #red(l1, x1, y1, #red(l2, x2, y2, r2))) { - #red( - #black(l, x, y, l1), - x1, - y1, - #black(l2, x2, y2, r2) - ) - }; - case (l, #red(#red(l1, x1, y1, r1), x2, y2, r2)) { - #red( - #black(l, x, y, l1), - x1, - y1, - #black(r1, x2, y2, r2) - ) - }; - case _ { - #black(left, x, y, right) - } - } - }; - - type ClashResolver = { old : A; new : A } -> A; - - func insertWith( - m : Tree, - compare : (K, K) -> Order.Order, - key : K, - val : V, - onClash : ClashResolver - ) : Tree { - func ins(tree : Tree) : Tree { - switch tree { - case (#black(left, x, y, right)) { - switch (compare(key, x)) { - case (#less) { - lbalance(ins left, x, y, right) - }; - case (#greater) { - rbalance(left, x, y, ins right) - }; - case (#equal) { - let newVal = onClash({ new = val; old = y }); - #black(left, key, newVal, right) - } - } - }; - case (#red(left, x, y, right)) { - switch (compare(key, x)) { - case (#less) { - #red(ins left, x, y, right) - }; - case (#greater) { - #red(left, x, y, ins right) - }; - case (#equal) { - let newVal = onClash { new = val; old = y }; - #red(left, key, newVal, right) - } - } - }; - case (#leaf) { - #red(#leaf, key, val, #leaf) - } - } - }; - switch (ins m) { - case (#red(left, x, y, right)) { - #black(left, x, y, right) - }; - case other { other } - } - }; - - public func swap( - m : Tree, - compare : (K, K) -> Order.Order, - key : K, - val : V - ) : (Tree, ?V) { - var oldVal : ?V = null; - func onClash(clash : { old : V; new : V }) : V { - oldVal := ?clash.old; - clash.new - }; - let res = insertWith(m, compare, key, val, onClash); - (res, oldVal) - }; - - public func add( - m : Tree, - compare : (K, K) -> Order.Order, - key : K, - val : V - ) : Tree = swap(m, compare, key, val).0; - - func balLeft(left : Tree, x : K, y : V, right : Tree) : Tree { - switch (left, right) { - case (#red(l1, x1, y1, r1), r) { - #red( - #black(l1, x1, y1, r1), - x, - y, - r - ) - }; - case (_, #black(l2, x2, y2, r2)) { - rbalance(left, x, y, #red(l2, x2, y2, r2)) - }; - case (_, #red(#black(l2, x2, y2, r2), x3, y3, r3)) { - #red( - #black(left, x, y, l2), - x2, - y2, - rbalance(r2, x3, y3, redden r3) - ) - }; - case _ { Runtime.trap "pure/Map.balLeft() impossible" } - } - }; - - func balRight(left : Tree, x : K, y : V, right : Tree) : Tree { - switch (left, right) { - case (l, #red(l1, x1, y1, r1)) { - #red( - l, - x, - y, - #black(l1, x1, y1, r1) - ) - }; - case (#black(l1, x1, y1, r1), r) { - lbalance(#red(l1, x1, y1, r1), x, y, r) - }; - case (#red(l1, x1, y1, #black(l2, x2, y2, r2)), r3) { - #red( - lbalance(redden l1, x1, y1, l2), - x2, - y2, - #black(r2, x, y, r3) - ) - }; - case _ { Runtime.trap "pure/Map.balRight() impossible" } - } - }; - - func append(left : Tree, right : Tree) : Tree { - switch (left, right) { - case (#leaf, _) { right }; - case (_, #leaf) { left }; - case ( - #red(l1, x1, y1, r1), - #red(l2, x2, y2, r2) - ) { - switch (append(r1, l2)) { - case (#red(l3, x3, y3, r3)) { - #red( - #red(l1, x1, y1, l3), - x3, - y3, - #red(r3, x2, y2, r2) - ) - }; - case r1l2 { - #red(l1, x1, y1, #red(r1l2, x2, y2, r2)) - } - } - }; - case (t1, #red(l2, x2, y2, r2)) { - #red(append(t1, l2), x2, y2, r2) - }; - case (#red(l1, x1, y1, r1), t2) { - #red(l1, x1, y1, append(r1, t2)) - }; - case (#black(l1, x1, y1, r1), #black(l2, x2, y2, r2)) { - switch (append(r1, l2)) { - case (#red(l3, x3, y3, r3)) { - #red( - #black(l1, x1, y1, l3), - x3, - y3, - #black(r3, x2, y2, r2) - ) - }; - case r1l2 { - balLeft( - l1, - x1, - y1, - #black(r1l2, x2, y2, r2) - ) - } - } - } - } - }; - - public func delete(m : Tree, compare : (K, K) -> Order.Order, key : K) : Tree = remove(m, compare, key).0; - - public func remove(tree : Tree, compare : (K, K) -> Order.Order, x : K) : (Tree, ?V) { - var y0 : ?V = null; - func delNode(left : Tree, x1 : K, y1 : V, right : Tree) : Tree { - switch (compare(x, x1)) { - case (#less) { - let newLeft = del left; - switch left { - case (#black(_, _, _, _)) { - balLeft(newLeft, x1, y1, right) - }; - case _ { - #red(newLeft, x1, y1, right) - } - } - }; - case (#greater) { - let newRight = del right; - switch right { - case (#black(_, _, _, _)) { - balRight(left, x1, y1, newRight) - }; - case _ { - #red(left, x1, y1, newRight) - } - } - }; - case (#equal) { - y0 := ?y1; - append(left, right) - } - } - }; - func del(tree : Tree) : Tree { - switch tree { - case (#red(left, x, y, right)) { - delNode(left, x, y, right) - }; - case (#black(left, x, y, right)) { - delNode(left, x, y, right) - }; - case (#leaf) { - tree - } - } - }; - switch (del(tree)) { - case (#red(left, x, y, right)) { (#black(left, x, y, right), y0) }; - case other { (other, y0) } - } - }; - - // Test helper - public func validate(rbMap : Map, comp : (K, K) -> Order.Order) { - ignore blackDepth(rbMap.root, comp) - }; - - func blackDepth(node : Tree, comp : (K, K) -> Order.Order) : Nat { - func checkNode(left : Tree, key : K, right : Tree) : Nat { - checkKey(left, func(x : K) : Bool { comp(x, key) == #less }); - checkKey(right, func(x : K) : Bool { comp(x, key) == #greater }); - let leftBlacks = blackDepth(left, comp); - let rightBlacks = blackDepth(right, comp); - assert (leftBlacks == rightBlacks); - leftBlacks - }; - switch node { - case (#leaf) 0; - case (#red(left, key, _, right)) { - let leftBlacks = checkNode(left, key, right); - assert (not isRed(left)); - assert (not isRed(right)); - leftBlacks - }; - case (#black(left, key, _, right)) { - checkNode(left, key, right) + 1 - } - } - }; - - func isRed(node : Tree) : Bool { - switch node { - case (#red(_, _, _, _)) true; - case _ false - } - }; - - func checkKey(node : Tree, isValid : K -> Bool) { - switch node { - case (#leaf) {}; - case (#red(_, key, _, _)) { - assert (isValid(key)) - }; - case (#black(_, key, _, _)) { - assert (isValid(key)) - } - } - } - }; - -} diff --git a/.mops/core@2.3.1/src/pure/Queue.mo b/.mops/core@2.3.1/src/pure/Queue.mo deleted file mode 100644 index e179de0..0000000 --- a/.mops/core@2.3.1/src/pure/Queue.mo +++ /dev/null @@ -1,659 +0,0 @@ -/// Double-ended queue of a generic element type `T`. -/// -/// The interface is purely functional, not imperative, and queues are immutable values. -/// In particular, Queue operations such as push and pop do not update their input queue but, instead, return the -/// value of the modified Queue, alongside any other data. -/// The input queue is left unchanged. -/// -/// Examples of use-cases: -/// Queue (FIFO) by using `pushBack()` and `popFront()`. -/// Stack (LIFO) by using `pushFront()` and `popFront()`. -/// -/// A Queue is internally implemented as two lists, a head access list and a (reversed) tail access list, -/// that are dynamically size-balanced by splitting. -/// -/// Construction: Create a new queue with the `empty()` function. -/// -/// Note on the costs of push and pop functions: -/// * Runtime: `O(1)` amortized costs, `O(size)` worst case cost per single call. -/// * Space: `O(1)` amortized costs, `O(size)` worst case cost per single call. -/// -/// `n` denotes the number of elements stored in the queue. -/// -/// Note that some operations that traverse the elements of the queue (e.g. `forEach`, `values`) preserve the order of the elements, -/// whereas others (e.g. `map`, `contains`) do NOT guarantee that the elements are visited in any order. -/// The order is undefined to avoid allocations, making these operations more efficient. -/// -/// ```motoko name=import -/// import Queue "mo:core/pure/Queue"; -/// ``` - -import Iter "../Iter"; -import List "List"; -import Order "../Order"; -import Types "../Types"; -import Array "../Array"; -import Prim "mo:⛔"; - -module { - /// @deprecated M0235 - type List = Types.Pure.List; - - /// Double-ended queue data type. - public type Queue = Types.Pure.Queue; - - /// Create a new empty queue. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func empty() : Queue = (null, 0, null); - - /// Determine whether a queue is empty. - /// Returns true if `queue` is empty, otherwise `false`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func isEmpty(self : Queue) : Bool = self.1 == 0; - - /// Create a new queue comprising a single element. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.singleton(25); - /// assert Queue.size(queue) == 1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func singleton(item : T) : Queue = (null, 1, ?(item, null)); - - /// Determine the number of elements contained in a queue. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.singleton(42); - /// assert Queue.size(queue) == 1; - /// } - /// ``` - /// - /// Runtime: `O(1)` in Release profile (compiled with `--release` flag), `O(size)` otherwise. - /// - /// Space: `O(1)`. - public func size(self : Queue) : Nat { - debug assert self.1 == List.size(self.0) + List.size(self.2); - self.1 - }; - - /// Check if a queue contains a specific element. - /// Returns true if the queue contains an element equal to `item` according to the `equal` function. - /// - /// Note: The order in which elements are visited is undefined, for performance reasons. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.contains(queue, Nat.equal, 2); - /// assert not Queue.contains(queue, Nat.equal, 4); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func contains(self : Queue, equal : (implicit : (T, T) -> Bool), item : T) : Bool = List.contains(self.0, equal, item) or List.contains(self.2, equal, item); - - /// Inspect the optional element on the front end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, the front element of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekFront(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func peekFront(self : Queue) : ?T = switch self { - case ((?(x, _), _, _) or (_, _, ?(x, null))) ?x; - case _ { debug assert List.isEmpty(self.2); null } - }; - - /// Inspect the optional element on the back end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, the back element of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// assert Queue.peekBack(queue) == ?2; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func peekBack(self : Queue) : ?T = switch self { - case ((_, _, ?(x, _)) or (?(x, null), _, _)) ?x; - case _ { debug assert List.isEmpty(self.0); null } - }; - - // helper to rebalance the queue after getting lopsided - func check(q : Queue) : Queue { - switch q { - case (null, n, r) { - let (a, b) = List.split(r, n / 2); - (List.reverse b, n, a) - }; - case (f, n, null) { - let (a, b) = List.split(f, n / 2); - (a, n, List.reverse b) - }; - case q q - } - }; - - /// Insert a new element on the front end of a queue. - /// Returns the new queue with `element` in the front followed by the elements of `queue`. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekFront(queue) == ?1; - /// assert Queue.peekBack(queue) == ?2; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: `O(size)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(size)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the queue. - public func pushFront(self : Queue, element : T) : Queue = check(?(element, self.0), self.1 + 1, self.2); - - /// Insert a new element on the back end of a queue. - /// Returns the new queue with all the elements of `queue`, followed by `element` on the back. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// assert Queue.peekBack(queue) == ?2; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: `O(size)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(size)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the queue. - public func pushBack(self : Queue, element : T) : Queue = check(self.0, self.1 + 1, ?(element, self.2)); - - /// Remove the element on the front end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, it returns a pair of - /// the first element and a new queue that contains all the remaining elements of `queue`. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko include=import - /// import Runtime "mo:core/Runtime"; - /// - /// persistent actor { - /// let initial = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// // initial queue with elements [1, 2] - /// switch (Queue.popFront(initial)) { - /// case null Runtime.trap "Empty queue impossible"; - /// case (?(frontElement, remainingQueue)) { - /// assert frontElement == 1; - /// assert Queue.size(remainingQueue) == 1 - /// } - /// } - /// } - /// ``` - /// - /// Runtime: `O(size)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(size)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the queue. - public func popFront(self : Queue) : ?(T, Queue) = if (self.1 == 0) null else switch self { - case (?(i, f), n, b) ?(i, (f, n - 1, b)); - case (null, _, ?(i, null)) ?(i, (null, 0, null)); - case _ popFront(check self) - }; - - /// Remove the element on the back end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, it returns a pair of - /// a new queue that contains the remaining elements of `queue` - /// and, as the second pair item, the removed back element. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko include=import - /// import Runtime "mo:core/Runtime"; - /// - /// persistent actor { - /// let initial = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// // initial queue with elements [1, 2] - /// let reduced = Queue.popBack(initial); - /// switch reduced { - /// case null Runtime.trap("Empty queue impossible"); - /// case (?result) { - /// let reducedQueue = result.0; - /// let removedElement = result.1; - /// assert removedElement == 2; - /// assert Queue.size(reducedQueue) == 1; - /// } - /// } - /// } - /// ``` - /// - /// Runtime: `O(size)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(size)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the queue. - public func popBack(self : Queue) : ?(Queue, T) = if (self.1 == 0) null else switch self { - case (f, n, ?(i, b)) ?((f, n - 1, b), i); - case (?(i, null), _, null) ?((null, 0, null), i); - case _ popBack(check self) - }; - - /// Turn an iterator into a queue, consuming it. - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([0, 1, 2, 3, 4].values()); - /// assert Queue.size(queue) == 5; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromIter(iter : Iter.Iter) : Queue { - let list = List.fromIter iter; - check(list, List.size list, null) - }; - - /// Convert an iterator to a queue, consuming it. - /// Example: - /// ```motoko include=import - /// persistent actor { - /// transient let iter = [0, 1, 2, 3, 4].values(); - /// - /// let queue = iter.toQueue(); - /// assert Queue.size(queue) == 5; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toQueue(self : Iter.Iter) : Queue { - fromIter(self) - }; - - /// Create a queue from an array. - /// Elements appear in the same order as in the array. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromArray(["A", "B", "C"]); - /// assert Queue.size(queue) == 3; - /// assert Queue.peekFront(queue) == ?"A"; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromArray(array : [T]) : Queue { - let list = List.fromArray array; - check(list, array.size(), null) - }; - - /// Create an immutable array from a queue. - /// Elements appear in the same order as in the queue (front to back). - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// - /// persistent actor { - /// let queue = Queue.fromArray(["A", "B", "C"]); - /// let array = Queue.toArray(queue); - /// assert array == ["A", "B", "C"]; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toArray(self : Queue) : [T] { - let iter = values(self); - Array.tabulate( - self.1, - func(i) { - switch (iter.next()) { - case null { - Prim.trap("pure/Queue.toArray: unexpected end of iterator") - }; - case (?value) { value } - } - } - ) - }; - - /// Convert a queue to an iterator of its elements in front-to-back order. - /// - /// Performance note: Creating the iterator needs `O(size)` runtime and space! - /// - /// Example: - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Iter.toArray(Queue.values(queue)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func values(self : Queue) : Iter.Iter = Iter.concat(List.values(self.0), List.values(List.reverse(self.2))); - - /// Compare two queues for equality using the provided equality function. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2].values()); - /// let queue2 = Queue.fromIter([1, 2].values()); - /// let queue3 = Queue.fromIter([1, 3].values()); - /// assert Queue.equal(queue1, queue2, Nat.equal); - /// assert not Queue.equal(queue1, queue3, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func equal(self : Queue, other : Queue, equal : (implicit : (T, T) -> Bool)) : Bool { - if (self.1 != other.1) { - return false - }; - let (iter1, iter2) = (values(self), values(other)); - loop { - switch (iter1.next(), iter2.next()) { - case (null, null) { return true }; - case (?v1, ?v2) { - if (not equal(v1, v2)) { return false } - }; - case (_, _) { return false } - } - } - }; - - /// Return true if the given predicate `f` is true for all queue - /// elements. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let allGreaterThanOne = Queue.all(queue, func n = n > 1); - /// assert not allGreaterThanOne; // false because 1 is not > 1 - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` as the current implementation uses `values` to iterate over the queue. - /// - /// *Runtime and space assumes that the `predicate` runs in `O(1)` time and space. - public func all(self : Queue, predicate : T -> Bool) : Bool { - for (item in values self) if (not (predicate item)) return false; - return true - }; - - /// Return true if there exists a queue element for which - /// the given predicate `f` is true. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let hasGreaterThanOne = Queue.any(queue, func n = n > 1); - /// assert hasGreaterThanOne; // true because 2 and 3 are > 1 - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` as the current implementation uses `values` to iterate over the queue. - /// - /// *Runtime and space assumes that the `predicate` runs in `O(1)` time and space. - public func any(self : Queue, predicate : T -> Bool) : Bool { - for (item in values self) if (predicate item) return true; - return false - }; - - /// Call the given function for its side effect, with each queue element in turn. - /// The order of visiting elements is front-to-back. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// var text = ""; - /// let queue = Queue.fromIter(["A", "B", "C"].values()); - /// Queue.forEach(queue, func n = text #= n); - /// assert text == "ABC"; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func forEach(self : Queue, f : T -> ()) = for (item in values self) f item; - - /// Call the given function `f` on each queue element and collect the results - /// in a new queue. - /// - /// Note: The order of visiting elements is undefined with the current implementation. - /// - /// Example: - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([0, 1, 2].values()); - /// let textQueue = Queue.map(queue, Nat.toText); - /// assert Iter.toArray(Queue.values(textQueue)) == ["0", "1", "2"]; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func map(self : Queue, f : T1 -> T2) : Queue { - let (fr, n, b) = self; - (List.map(fr, f), n, List.map(b, f)) - }; - - /// Create a new queue with only those elements of the original queue for which - /// the given function (often called the _predicate_) returns true. - /// - /// Note: The order of visiting elements is undefined with the current implementation. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([0, 1, 2, 1].values()); - /// let filtered = Queue.filter(queue, func n = n != 1); - /// assert Queue.size(filtered) == 2; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func filter(self : Queue, predicate : T -> Bool) : Queue { - let (fr, _, b) = self; - let front = List.filter(fr, predicate); - let back = List.filter(b, predicate); - check(front, List.size front + List.size back, back) - }; - - /// Call the given function on each queue element, and collect the non-null results - /// in a new queue. - /// - /// Note: The order of visiting elements is undefined with the current implementation. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let doubled = Queue.filterMap( - /// queue, - /// func n = if (n > 1) ?(n * 2) else null - /// ); - /// assert Queue.size(doubled) == 2; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func filterMap(self : Queue, f : T -> ?U) : Queue { - let (fr, _n, b) = self; - let front = List.filterMap(fr, f); - let back = List.filterMap(b, f); - check(front, List.size front + List.size back, back) - }; - - /// Convert a queue to its text representation using the provided conversion function. - /// This function is meant to be used for debugging and testing purposes. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.toText(queue, Nat.toText) == "PureQueue[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func toText(self : Queue, f : (implicit : (toText : T -> Text))) : Text { - var text = "PureQueue["; - func add(item : T) { - if (text.size() > 10) text #= ", "; - text #= f(item) - }; - List.forEach(self.0, add); - List.forEach(List.reverse(self.2), add); - text # "]" - }; - - /// Compare two queues using lexicographic ordering specified by argument function `compareItem`. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2].values()); - /// let queue2 = Queue.fromIter([1, 3].values()); - /// assert Queue.compare(queue1, queue2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that argument `compareItem` runs in `O(1)` time and space. - public func compare(self : Queue, other : Queue, compareItem : (implicit : (compare : (T, T) -> Order.Order))) : Order.Order { - let (i1, i2) = (values self, values other); - loop switch (i1.next(), i2.next()) { - case (?v1, ?v2) switch (compareItem(v1, v2)) { - case (#equal) (); - case c return c - }; - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater - } - }; - - /// Reverse the order of elements in a queue. - /// This operation is cheap, it does NOT require copying the elements. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let reversed = Queue.reverse(queue); - /// assert Queue.peekFront(reversed) == ?3; - /// assert Queue.peekBack(reversed) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func reverse(self : Queue) : Queue = (self.2, self.1, self.0) -} diff --git a/.mops/core@2.3.1/src/pure/RealTimeQueue.mo b/.mops/core@2.3.1/src/pure/RealTimeQueue.mo deleted file mode 100644 index adeb25f..0000000 --- a/.mops/core@2.3.1/src/pure/RealTimeQueue.mo +++ /dev/null @@ -1,1175 +0,0 @@ -/// Double-ended immutable queue with guaranteed `O(1)` push/pop operations (caveat: high constant factor). -/// For a default immutable queue implementation, see `pure/Queue`. -/// -/// This module provides an alternative implementation with better worst-case performance for single operations, e.g. `pushBack` and `popFront`. -/// These operations are always constant time, `O(1)`, which eliminates spikes in performance of `pure/Queue` operations -/// that are caused by the amortized nature of the `pure/Queue` implementation, which can lead to `O(n)` worst-case performance for a single operation. -/// The spikes in performance can cause a single message to take multiple more rounds to complete than most other messages. -/// -/// However, the `O(1)` operations come at a cost of higher constant factor than the `pure/Queue` implementation: -/// - 'pop' operations are on average 3x more expensive -/// - 'push' operations are on average 8x more expensive -/// -/// For better performance across multiple operations and when the spikes in single operations are not a problem, use `pure/Queue`. -/// For guaranteed `O(1)` operations, use `pure/RealTimeQueue`. -/// -/// --- -/// -/// The interface is purely functional, not imperative, and queues are immutable values. -/// In particular, Queue operations such as push and pop do not update their input queue but, instead, return the -/// value of the modified Queue, alongside any other data. -/// The input queue is left unchanged. -/// -/// Examples of use-cases: -/// - Queue (FIFO) by using `pushBack()` and `popFront()`. -/// - Stack (LIFO) by using `pushFront()` and `popFront()`. -/// - Deque (double-ended queue) by using any combination of push/pop operations on either end. -/// -/// A Queue is internally implemented as a real-time double-ended queue based on the paper -/// "Real-Time Double-Ended Queue Verified (Proof Pearl)". The implementation maintains -/// worst-case constant time `O(1)` for push/pop operations through gradual rebalancing steps. -/// -/// Construction: Create a new queue with the `empty()` function. -/// -/// Note that some operations that traverse the elements of the queue (e.g. `forEach`, `values`) preserve the order of the elements, -/// whereas others (e.g. `map`, `contains`) do NOT guarantee that the elements are visited in any order. -/// The order is undefined to avoid allocations, making these operations more efficient. -/// -/// ```motoko name=import -/// import Queue "mo:core/pure/RealTimeQueue"; -/// ``` - -import Types "../Types"; -import List "List"; -import Option "../Option"; -import { trap } "../Runtime"; -import Iter "../Iter"; - -module { - /// The real-time queue data structure can be in one of the following states: - /// - /// - `#empty`: the queue is empty - /// - `#one`: the queue contains a single element - /// - `#two`: the queue contains two elements - /// - `#three`: the queue contains three elements - /// - `#idles`: the queue is in the idle state, where `l` and `r` are non-empty stacks of elements fulfilling the size invariant - /// - `#rebal`: the queue is in the rebalancing state - public type Queue = { - #empty; - #one : T; - #two : (T, T); - #three : (T, T, T); - #idles : (Idle, Idle); - #rebal : States - }; - - /// Create a new empty queue. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func empty() : Queue = #empty; - - /// Determine whether a queue is empty. - /// Returns true if `queue` is empty, otherwise `false`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func isEmpty(self : Queue) : Bool = switch self { - case (#empty) true; - case _ false - }; - - /// Create a new queue comprising a single element. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.singleton(25); - /// assert Queue.size(queue) == 1; - /// assert Queue.peekFront(queue) == ?25; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func singleton(element : T) : Queue = #one(element); - - /// Determine the number of elements contained in a queue. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.singleton(42); - /// assert Queue.size(queue) == 1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func size(self : Queue) : Nat = switch self { - case (#empty) 0; - case (#one _) 1; - case (#two _) 2; - case (#three _) 3; - case (#idles((l, nL), (r, nR))) { - debug assert Stacks.size(l) == nL and Stacks.size(r) == nR; - nL + nR - }; - case (#rebal(_, big, small)) BigState.size(big) + SmallState.size(small) - }; - - /// Test if a queue contains a given value. - /// Returns true if the queue contains the item, otherwise false. - /// - /// Note: The order in which elements are visited is undefined, for performance reasons. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// assert Queue.contains(queue, Nat.equal, 1); - /// assert not Queue.contains(queue, Nat.equal, 3); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func contains(self : Queue, equal : (implicit : (T, T) -> Bool), item : T) : Bool = switch self { - case (#empty) false; - case (#one(x)) equal(x, item); - case (#two(x, y)) equal(x, item) or equal(y, item); - case (#three(x, y, z)) equal(x, item) or equal(y, item) or equal(z, item); - case (#idles(((l1, l2), _), ((r1, r2), _))) List.contains(l1, equal, item) or List.contains(l2, equal, item) or List.contains(r2, equal, item) or List.contains(r1, equal, item); // note that the order of the right stack is reversed, but for this operation it does not matter - case (#rebal(_, big, small)) { - let (extraB, _, (oldB1, oldB2), _) = BigState.current(big); - let (extraS, _, (oldS1, oldS2), _) = SmallState.current(small); - // note that the order of one of the stacks is reversed (depending on the `direction` field), but for this operation it does not matter - List.contains(extraB, equal, item) or List.contains(oldB1, equal, item) or List.contains(oldB2, equal, item) or List.contains(extraS, equal, item) or List.contains(oldS1, equal, item) or List.contains(oldS2, equal, item) - } - }; - - /// Inspect the optional element on the front end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, the front element of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekFront(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func peekFront(self : Queue) : ?T = switch self { - case (#idles((l, _), _)) Stacks.first(l); - case (#rebal(dir, big, small)) switch dir { - case (#left) ?SmallState.peek(small); - case (#right) ?BigState.peek(big) - }; - case (#empty) null; - case (#one(x)) ?x; - case (#two(x, _)) ?x; - case (#three(x, _, _)) ?x - }; - - /// Inspect the optional element on the back end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, the back element of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekBack(queue) == ?2; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func peekBack(self : Queue) : ?T = switch self { - case (#idles(_, (r, _))) Stacks.first(r); - case (#rebal(dir, big, small)) switch dir { - case (#left) ?BigState.peek(big); - case (#right) ?SmallState.peek(small) - }; - case (#empty) null; - case (#one(x)) ?x; - case (#two(_, y)) ?y; - case (#three(_, _, z)) ?z - }; - - /// Insert a new element on the front end of a queue. - /// Returns the new queue with `element` in the front followed by the elements of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekFront(queue) == ?1; - /// assert Queue.peekBack(queue) == ?2; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: `O(1)` worst-case! - /// - /// Space: `O(1)` worst-case! - public func pushFront(self : Queue, element : T) : Queue = switch self { - case (#idles(l0, rnR)) { - let lnL = Idle.push(l0, element); // enque the element to the left end - // check if the size invariant still holds - if (3 * rnR.1 >= lnL.1) { - debug assert 3 * lnL.1 >= rnR.1; - #idles(lnL, rnR) - } else { - // initiate the rebalancing process - let (l, nL) = lnL; - let (r, nR) = rnR; - let targetSizeL = nL - nR - 1 : Nat; - let targetSizeR = 2 * nR + 1; - debug assert targetSizeL + targetSizeR == nL + nR; - let big = #big1(Current.new(l, targetSizeL), l, null, targetSizeL); - let small = #small1(Current.new(r, targetSizeR), r, null); - let states = (#right, big, small); - let states6 = States.step(States.step(States.step(States.step(States.step(States.step(states)))))); - #rebal(states6) - } - }; - // if the queue is in the middle of a rebalancing process: push the element and advance the rebalancing process by 4 steps - // move back into the idle state if the rebalancing is done - case (#rebal(dir, big0, small0)) switch dir { - case (#right) { - let big = BigState.push(big0, element); - let states4 = States.step(States.step(States.step(States.step((#right, big, small0))))); - debug assert states4.0 == #right; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(big, small); - #idles(big, small) - }; - case _ #rebal(states4) - } - }; - case (#left) { - let small = SmallState.push(small0, element); - let states4 = States.step(States.step(States.step(States.step((#left, big0, small))))); - debug assert states4.0 == #left; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(small, big); - #idles(small, big) // swapped because dir=left - }; - case _ #rebal(states4) - } - } - }; - case (#empty) #one(element); - case (#one(y)) #two(element, y); - case (#two(y, z)) #three(element, y, z); - case (#three(a, b, c)) { - let i1 = ((?(element, ?(a, null)), null), 2); - let i2 = ((?(c, ?(b, null)), null), 2); - #idles(i1, i2) - } - }; - - /// Insert a new element on the back end of a queue. - /// Returns the new queue with all the elements of `queue`, followed by `element` on the back. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// assert Queue.peekBack(queue) == ?2; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: `O(1)` worst-case! - /// - /// Space: `O(1)` worst-case! - public func pushBack(self : Queue, element : T) : Queue = switch self { - // Equivalent to: `reverse(pushFront(reverse(queue), element))`. Inlined for performance. - case (#idles(rnR, l0)) { - // ^ reversed input - let lnL = Idle.push(l0, element); - if (3 * rnR.1 >= lnL.1) { - debug assert 3 * lnL.1 >= rnR.1; - #idles(rnR, lnL) // reversed output - } else { - let (l, nL) = lnL; - let (r, nR) = rnR; - let targetSizeL = nL - nR - 1 : Nat; - let targetSizeR = 2 * nR + 1; - debug assert targetSizeL + targetSizeR == nL + nR; - let big = #big1(Current.new(l, targetSizeL), l, null, targetSizeL); - let small = #small1(Current.new(r, targetSizeR), r, null); - let states = (#left, big, small); // reversed output - let states6 = States.step(States.step(States.step(States.step(States.step(States.step(states)))))); - #rebal(states6) - } - }; - case (#rebal(dir, big0, small0)) switch dir { - case (#left) { - // ^ reversed input - let big = BigState.push(big0, element); - let states4 = States.step(States.step(States.step(States.step((#left, big, small0))))); // reversed output - debug assert states4.0 == #left; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(big, small); - #idles(small, big) // reversed output - }; - case _ #rebal(states4) - } - }; - case (#right) { - // ^ reversed input - let small = SmallState.push(small0, element); - let states4 = States.step(States.step(States.step(States.step((#right, big0, small))))); // reversed output - debug assert states4.0 == #right; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(small, big); - #idles(big, small) // reversed output - }; - case _ #rebal(states4) - } - } - }; - case (#empty) #one(element); - case (#one(y)) #two(y, element); - case (#two(y, z)) #three(y, z, element); - case (#three(a, b, c)) { - let i1 = ((?(a, ?(b, null)), null), 2); - let i2 = ((?(element, ?(c, null)), null), 2); - #idles(i1, i2) - } - }; - - /// Remove the element on the front end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, it returns a pair of - /// the first element and a new queue that contains all the remaining elements of `queue`. - /// - /// Example: - /// ```motoko include=import - /// import Runtime "mo:core/Runtime"; - /// - /// persistent actor { - /// do { - /// let initial = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// let ?(frontElement, remainingQueue) = Queue.popFront(initial) else Runtime.trap "Empty queue impossible"; - /// assert frontElement == 1; - /// assert Queue.size(remainingQueue) == 1; - /// } - /// } - /// ``` - /// - /// Runtime: `O(1)` worst-case! - /// - /// Space: `O(1)` worst-case! - public func popFront(self : Queue) : ?(T, Queue) = switch self { - case (#idles(l0, rnR)) { - let (x, lnL) = Idle.pop(l0); - if (3 * lnL.1 >= rnR.1) { - ?(x, #idles(lnL, rnR)) - } else if (lnL.1 >= 1) { - let (l, nL) = lnL; - let (r, nR) = rnR; - let targetSizeL = 2 * nL + 1; - let targetSizeR = nR - nL - 1 : Nat; - debug assert targetSizeL + targetSizeR == nL + nR; - let small = #small1(Current.new(l, targetSizeL), l, null); - let big = #big1(Current.new(r, targetSizeR), r, null, targetSizeR); - let states = (#left, big, small); - let states6 = States.step(States.step(States.step(States.step(States.step(States.step(states)))))); - ?(x, #rebal(states6)) - } else { - ?(x, Stacks.smallqueue(rnR.0)) - } - }; - case (#rebal(dir, big0, small0)) switch dir { - case (#left) { - let (x, small) = SmallState.pop(small0); - let states4 = States.step(States.step(States.step(States.step((#left, big0, small))))); - debug assert states4.0 == #left; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(small, big); - ?(x, #idles(small, big)) - }; - case _ ?(x, #rebal(states4)) - } - }; - case (#right) { - let (x, big) = BigState.pop(big0); - let states4 = States.step(States.step(States.step(States.step((#right, big, small0))))); - debug assert states4.0 == #right; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(big, small); - ?(x, #idles(big, small)) - }; - case _ ?(x, #rebal(states4)) - } - } - }; - case (#empty) null; - case (#one(x)) ?(x, #empty); - case (#two(x, y)) ?(x, #one(y)); - case (#three(x, y, z)) ?(x, #two(y, z)) - }; - - /// Remove the element on the back end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, it returns a pair of - /// a new queue that contains the remaining elements of `queue` - /// and, as the second pair item, the removed back element. - /// - /// Example: - /// ```motoko include=import - /// import Runtime "mo:core/Runtime"; - /// - /// persistent actor { - /// do { - /// let initial = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// let ?(reducedQueue, removedElement) = Queue.popBack(initial) else Runtime.trap "Empty queue impossible"; - /// assert removedElement == 2; - /// assert Queue.size(reducedQueue) == 1; - /// } - /// } - /// ``` - /// - /// Runtime: `O(1)` worst-case! - /// - /// Space: `O(1)` worst-case! - public func popBack(self : Queue) : ?(Queue, T) = switch self { - // Equivalent to: - // = do ? { let (x, queue2) = popFront(reverse(queue))!; (reverse(queue2), x) }; - // Inlined for performance. - case (#idles(rnR, l0)) { - // ^ reversed input - let (x, lnL) = Idle.pop(l0); - if (3 * lnL.1 >= rnR.1) { - ?(#idles(rnR, lnL), x) // reversed output - } else if (lnL.1 >= 1) { - let (l, nL) = lnL; - let (r, nR) = rnR; - let targetSizeL = 2 * nL + 1; - let targetSizeR = nR - nL - 1 : Nat; - debug assert targetSizeL + targetSizeR == nL + nR; - let small = #small1(Current.new(l, targetSizeL), l, null); - let big = #big1(Current.new(r, targetSizeR), r, null, targetSizeR); - let states = (#right, big, small); // reversed output - let states6 = States.step(States.step(States.step(States.step(States.step(States.step(states)))))); - ?(#rebal(states6), x) - } else { - ?(Stacks.smallqueueReversed(rnR.0), x) // reversed output - } - }; - case (#rebal(dir, big0, small0)) switch dir { - case (#right) { - // ^ reversed input - let (x, small) = SmallState.pop(small0); - let states4 = States.step(States.step(States.step(States.step((#right, big0, small))))); // reversed output - debug assert states4.0 == #right; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(big, small); - ?(#idles(big, small), x) // reversed output - }; - case _ ?(#rebal(states4), x) - } - }; - case (#left) { - // ^ reversed input - let (x, big) = BigState.pop(big0); - let states4 = States.step(States.step(States.step(States.step((#left, big, small0))))); // reversed output - debug assert states4.0 == #left; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(small, big); - ?(#idles(small, big), x) // reversed output - }; - case _ ?(#rebal(states4), x) - } - } - }; - case (#empty) null; - case (#one(x)) ?(#empty, x); - case (#two(x, y)) ?(#one(x), y); - case (#three(x, y, z)) ?(#two(x, y), z) - }; - - /// Turn an iterator into a queue, consuming it. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([0, 1, 2, 3, 4].values()); - /// assert Queue.peekFront(queue) == ?0; - /// assert Queue.peekBack(queue) == ?4; - /// assert Queue.size(queue) == 5; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func fromIter(iter : Iter) : Queue { - var queue = empty(); - Iter.forEach(iter, func(t : T) = queue := pushBack(queue, t)); - queue - }; - - /// Convert an iterator into a queue, consuming the iterator. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// transient let iter = [0, 1, 2, 3, 4].values(); - /// - /// let queue = iter.toQueue(); - /// - /// assert Queue.peekFront(queue) == ?0; - /// assert Queue.peekBack(queue) == ?4; - /// assert Queue.size(queue) == 5; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func toQueue(self : Iter) : Queue { - fromIter(self) - }; - - /// Create an iterator over the elements in the queue. The order of the elements is from front to back. - /// - /// Example: - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Iter.toArray(Queue.values(queue)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(1)` to create the iterator and for each `next()` call. - /// - /// Space: `O(1)` to create the iterator and for each `next()` call. - public func values(self : Queue) : Iter.Iter { - object { - var current = self; - public func next() : ?T { - switch (popFront(current)) { - case null null; - case (?result) { - current := result.1; - ?result.0 - } - } - } - } - }; - - /// Compare two queues for equality using a provided equality function to compare their elements. - /// Two queues are considered equal if they contain the same elements in the same order. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2, 3].values()); - /// let queue2 = Queue.fromIter([1, 2, 3].values()); - /// let queue3 = Queue.fromIter([1, 3, 2].values()); - /// assert Queue.equal(queue1, queue2, Nat.equal); - /// assert not Queue.equal(queue1, queue3, Nat.equal); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func equal(self : Queue, other : Queue, equal : (implicit : (T, T) -> Bool)) : Bool { - if (size(self) != size(other)) { - return false - }; - func go(self : Queue, other : Queue, equal : (T, T) -> Bool) : Bool = switch (popFront self, popFront other) { - case (null, null) true; - case (?(x1, tail1), ?(x2, tail2)) equal(x1, x2) and go(tail1, tail2, equal); // Note that this is tail recursive (`and` is expanded to `if`). - case _ false - }; - go(self, other, equal) - }; - - /// Compare two queues lexicographically using a provided comparison function to compare their elements. - /// Returns `#less` if `queue1` is lexicographically less than `queue2`, `#equal` if they are equal, and `#greater` otherwise. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2, 3].values()); - /// let queue2 = Queue.fromIter([1, 2, 4].values()); - /// assert Queue.compare(queue1, queue2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func compare(self : Queue, other : Queue, compareItem : (implicit : (compare : (T, T) -> Types.Order))) : Types.Order = switch (popFront self, popFront other) { - case (null, null) #equal; - case (null, _) #less; - case (_, null) #greater; - case (?(x1, selfTail), ?(x2, otherTail)) { - switch (compareItem(x1, x2)) { - case (#equal) compare(selfTail, otherTail, compareItem); - case order order - } - } - }; - - /// Return true if the given predicate is true for all queue elements. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([2, 4, 6].values()); - /// assert Queue.all(queue, func n = n % 2 == 0); - /// assert not Queue.all(queue, func n = n > 4); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` as the current implementation uses `values` to iterate over the queue. - /// - /// *Runtime and space assumes that the `predicate` runs in `O(1)` time and space. - public func all(self : Queue, predicate : T -> Bool) : Bool = switch self { - case (#empty) true; - case (#one(x)) predicate x; - case (#two(x, y)) predicate x and predicate y; - case (#three(x, y, z)) predicate x and predicate y and predicate z; - case _ { - for (item in values self) if (not (predicate item)) return false; - return true - } - }; - - /// Return true if the given predicate is true for any queue element. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.any(queue, func n = n > 2); - /// assert not Queue.any(queue, func n = n > 3); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` as the current implementation uses `values` to iterate over the queue. - /// - /// *Runtime and space assumes that the `predicate` runs in `O(1)` time and space. - public func any(self : Queue, predicate : T -> Bool) : Bool = switch self { - case (#empty) false; - case (#one(x)) predicate x; - case (#two(x, y)) predicate x or predicate y; - case (#three(x, y, z)) predicate x or predicate y or predicate z; - case _ { - for (item in values self) if (predicate item) return true; - return false - } - }; - - /// Call the given function for its side effect on each queue element in order: from front to back. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// persistent actor { - /// var text = ""; - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// Queue.forEach(queue, func n = text #= Nat.toText(n)); - /// assert text == "123"; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func forEach(self : Queue, f : T -> ()) = switch self { - case (#empty) (); - case (#one(x)) f x; - case (#two(x, y)) { f x; f y }; - case (#three(x, y, z)) { f x; f y; f z }; - // Preserve the order when visiting the elements. Note that the #idles case would require reversing the second stack. - case _ { - for (t in values self) f t - } - }; - - /// Create a new queue by applying the given function to each element of the original queue. - /// - /// Note: The order of visiting elements is undefined with the current implementation. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let mapped = Queue.map(queue, func n = n * 2); - /// assert Queue.size(mapped) == 3; - /// assert Queue.peekFront(mapped) == ?2; - /// assert Queue.peekBack(mapped) == ?6; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func map(self : Queue, f : T1 -> T2) : Queue = switch self { - case (#empty) #empty; - case (#one(x)) #one(f x); - case (#two(x, y)) #two(f x, f y); - case (#three(x, y, z)) #three(f x, f y, f z); - case (#idles(l, r)) #idles(Idle.map(l, f), Idle.map(r, f)); - case (#rebal(_)) { - // No reason to rebuild the #rebal state. - // future work: It could be further optimized by building a balanced #idles state directly since we know the sizes. - var q = empty(); - for (t in values self) q := pushBack(q, f t); - q - } - }; - - /// Create a new queue with only those elements of the original queue for which - /// the given predicate returns true. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3, 4].values()); - /// let filtered = Queue.filter(queue, func n = n % 2 == 0); - /// assert Queue.size(filtered) == 2; - /// assert Queue.peekFront(filtered) == ?2; - /// assert Queue.peekBack(filtered) == ?4; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func filter(self : Queue, predicate : T -> Bool) : Queue { - var q = empty(); - for (t in values self) if (predicate t) q := pushBack(q, t); - q - }; - - /// Create a new queue by applying the given function to each element of the original queue - /// and collecting the results for which the function returns a non-null value. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3, 4].values()); - /// let filtered = Queue.filterMap(queue, func n = if (n % 2 == 0) { ?n } else null); - /// assert Queue.size(filtered) == 2; - /// assert Queue.peekFront(filtered) == ?2; - /// assert Queue.peekBack(filtered) == ?4; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that f runs in `O(1)` time and space. - public func filterMap(self : Queue, f : T -> ?U) : Queue { - var q = empty(); - for (t in values self) { - switch (f t) { - case (?x) q := pushBack(q, x); - case null () - } - }; - q - }; - - /// Create a `Text` representation of a queue for debugging purposes. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.toText(queue, Nat.toText) == "RealTimeQueue[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that f runs in `O(1)` time and space. - public func toText(self : Queue, f : (implicit : (toText : T -> Text))) : Text { - var text = "RealTimeQueue["; - var first = true; - for (t in values self) { - if (first) first := false else text #= ", "; - text #= f(t) - }; - text # "]" - }; - - /// Reverse the order of elements in a queue. - /// This operation is cheap, it does NOT require copying the elements. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let reversed = Queue.reverse(queue); - /// assert Queue.peekFront(reversed) == ?3; - /// assert Queue.peekBack(reversed) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func reverse(self : Queue) : Queue = switch self { - case (#idles(l, r)) #idles(r, l); - case (#rebal(#left, big, small)) #rebal(#right, big, small); - case (#rebal(#right, big, small)) #rebal(#left, big, small); - case (#empty) self; - case (#one(_)) self; - case (#two(x, y)) #two(y, x); - case (#three(x, y, z)) #three(z, y, x) - }; - - type Stacks = (left : List, right : List); - - module Stacks { - public func push((left, right) : Stacks, t : T) : Stacks = (?(t, left), right); - - public func pop(stacks : Stacks) : Stacks = switch stacks { - case (?(_, leftTail), right) (leftTail, right); - case (null, ?(_, rightTail)) (null, rightTail); - case (null, null) stacks - }; - - public func first((left, right) : Stacks) : ?T = switch (left) { - case (?(h, _)) ?h; - case (null) do ? { right!.0 } - }; - - public func unsafeFirst((left, right) : Stacks) : T = switch (left) { - case (?(h, _)) h; - case (null) Option.unwrap(right).0 - }; - - public func isEmpty((left, right) : Stacks) : Bool = List.isEmpty(left) and List.isEmpty(right); - - public func size((left, right) : Stacks) : Nat = List.size(left) + List.size(right); - - public func smallqueue((left, right) : Stacks) : Queue = switch (left, right) { - case (null, null) #empty; - case (null, ?(x, null)) #one(x); - case (?(x, null), null) #one(x); - case (null, ?(x, ?(y, null))) #two(y, x); - case (?(x, null), ?(y, null)) #two(y, x); - case (?(x, ?(y, null)), null) #two(y, x); - case (null, ?(x, ?(y, ?(z, null)))) #three(z, y, x); - case (?(x, ?(y, ?(z, null))), null) #three(z, y, x); - case (?(x, ?(y, null)), ?(z, null)) #three(z, y, x); - case (?(x, null), ?(y, ?(z, null))) #three(z, y, x); - case _ (trap "Queue.Stacks.smallqueue() impossible") - }; - - public func smallqueueReversed((left, right) : Stacks) : Queue = switch (left, right) { - case (null, null) #empty; - case (null, ?(x, null)) #one(x); - case (?(x, null), null) #one(x); - case (null, ?(x, ?(y, null))) #two(x, y); - case (?(x, null), ?(y, null)) #two(x, y); - case (?(x, ?(y, null)), null) #two(x, y); - case (null, ?(x, ?(y, ?(z, null)))) #three(x, y, z); - case (?(x, ?(y, ?(z, null))), null) #three(x, y, z); - case (?(x, ?(y, null)), ?(z, null)) #three(x, y, z); - case (?(x, null), ?(y, ?(z, null))) #three(x, y, z); - case _ (trap "Queue.Stacks.smallqueueReversed() impossible") - }; - public func map((left, right) : Stacks, f : T -> U) : Stacks = (List.map(left, f), List.map(right, f)) - }; - - /// Represents an end of the queue that is not in a rebalancing process. It is a stack and its size. - type Idle = (stacks : Stacks, size : Nat); - module Idle { - public func push((stacks, size) : Idle, t : T) : Idle = (Stacks.push(stacks, t), 1 + size); - public func pop((stacks, size) : Idle) : (T, Idle) = (Stacks.unsafeFirst(stacks), (Stacks.pop(stacks), size - 1 : Nat)); - public func peek((stacks, _) : Idle) : T = Stacks.unsafeFirst(stacks); - - public func map((stacks, size) : Idle, f : T -> U) : Idle = (Stacks.map(stacks, f), size) - }; - - /// Stores information about operations that happen during rebalancing but which have not become part of the old state that is being rebalanced. - /// - /// - `extra`: newly added elements - /// - `extraSize`: size of `extra` - /// - `old`: elements contained before the rebalancing process - /// - `targetSize`: the number of elements which will be contained after the rebalancing is finished - type Current = (extra : List, extraSize : Nat, old : Stacks, targetSize : Nat); - - module Current { - public func new(old : Stacks, targetSize : Nat) : Current = (null, 0, old, targetSize); - - public func push((extra, extraSize, old, targetSize) : Current, t : T) : Current = (?(t, extra), 1 + extraSize, old, targetSize); - - public func pop((extra, extraSize, old, targetSize) : Current) : (T, Current) = switch (extra) { - case (?(h, t)) (h, (t, extraSize - 1 : Nat, old, targetSize)); - case (null) (Stacks.unsafeFirst(old), (null, extraSize, Stacks.pop(old), targetSize - 1 : Nat)) - }; - - public func peek((extra, _, old, _) : Current) : T = switch (extra) { - case (?(h, _)) h; - case (null) Stacks.unsafeFirst(old) - }; - - public func size((_, extraSize, _, targetSize) : Current) : Nat = extraSize + targetSize - }; - - /// The bigger end of the queue during rebalancing. It is used to split the bigger end of the queue into the new big end and a portion to be added to the small end. Can be in one of the following states: - /// - /// - `#big1(cur, big, aux, n)`: Initial state. Using the step function it takes `n`-elements from the `big` stack and puts them to `aux` in reversed order. `#big1(cur, x1 .. xn : bigTail, [], n) ->* #big1(cur, bigTail, xn .. x1, 0)`. The `bigTail` is later given to the `small` end. - /// - `#big2(common)`: Is used to reverse the elements from the previous phase to restore the original order. `common = #copy(cur, xn .. x1, [], 0) ->* #copy(cur, [], x1 .. xn, n)`. - type BigState = { - #big1 : (Current, Stacks, List, Nat); - #big2 : CommonState - }; - - module BigState { - public func push(big : BigState, t : T) : BigState = switch big { - case (#big1(cur, big, aux, n)) #big1(Current.push(cur, t), big, aux, n); - case (#big2(state)) #big2(CommonState.push(state, t)) - }; - - public func pop(big : BigState) : (T, BigState) = switch big { - case (#big1(cur, big, aux, n)) { - let (x, cur2) = Current.pop(cur); - (x, #big1(cur2, big, aux, n)) - }; - case (#big2(state)) { - let (x, state2) = CommonState.pop(state); - (x, #big2(state2)) - } - }; - - public func peek(big : BigState) : T = switch big { - case (#big1(cur, _, _, _)) Current.peek(cur); - case (#big2(state)) CommonState.peek(state) - }; - - public func step(big : BigState) : BigState = switch big { - case (#big1(cur, big, aux, n)) { - if (n == 0) - #big2(CommonState.norm(#copy(cur, aux, null, 0))) else - #big1(cur, Stacks.pop(big), ?(Stacks.unsafeFirst(big), aux), n - 1 : Nat) - }; - case (#big2(state)) #big2(CommonState.step(state)) - }; - - public func size(big : BigState) : Nat = switch big { - case (#big1(cur, _, _, _)) Current.size(cur); - case (#big2(state)) CommonState.size(state) - }; - - public func current(big : BigState) : Current = switch big { - case (#big1(cur, _, _, _)) cur; - case (#big2(state)) CommonState.current(state) - } - }; - - /// The smaller end of the queue during rebalancing. Can be in one of the following states: - /// - /// - `#small1(cur, small, aux)`: Initial state. Using the step function the original elements are reversed. `#small1(cur, s1 .. sn, []) ->* #small1(cur, [], sn .. s1)`, note that `aux` is initially empty, at the end contains the reversed elements from the small stack. - /// - `#small2(cur, aux, big, new, size)`: Using the step function the newly transfered tail from the bigger end is reversed on top of the `new` list. `#small2(cur, sn .. s1, b1 .. bm, [], 0) ->* #small2(cur, sn .. s1, [], bm .. b1, m)`, note that `aux` is the reversed small stack from the previous phase, `new` is initially empty, `size` corresponds to the size of `new`. - /// - `#small3(common)`: Is used to reverse the elements from the two previous phases again to get them again in the original order. `#copy(cur, sn .. s1, bm .. b1, m) ->* #copy(cur, [], s1 .. sn : bm .. b1, n + m)`, note that the correct order of the elements from the big stack is reversed. - type SmallState = { - #small1 : (Current, Stacks, List); - #small2 : (Current, List, Stacks, List, Nat); - #small3 : CommonState - }; - - module SmallState { - public func push(state : SmallState, t : T) : SmallState = switch state { - case (#small1(cur, small, aux)) #small1(Current.push(cur, t), small, aux); - case (#small2(cur, aux, big, new, newN)) #small2(Current.push(cur, t), aux, big, new, newN); - case (#small3(common)) #small3(CommonState.push(common, t)) - }; - - public func pop(state : SmallState) : (T, SmallState) = switch state { - case (#small1(cur0, small, aux)) { - let (t, cur) = Current.pop(cur0); - (t, #small1(cur, small, aux)) - }; - case (#small2(cur0, aux, big, new, newN)) { - let (t, cur) = Current.pop(cur0); - (t, #small2(cur, aux, big, new, newN)) - }; - case (#small3(common0)) { - let (t, common) = CommonState.pop(common0); - (t, #small3(common)) - } - }; - - public func peek(state : SmallState) : T = switch state { - case (#small1(cur, _, _)) Current.peek(cur); - case (#small2(cur, _, _, _, _)) Current.peek(cur); - case (#small3(common)) CommonState.peek(common) - }; - - public func step(state : SmallState) : SmallState = switch state { - case (#small1(cur, small, aux)) { - if (Stacks.isEmpty(small)) state else #small1(cur, Stacks.pop(small), ?(Stacks.unsafeFirst(small), aux)) - }; - case (#small2(cur, aux, big, new, newN)) { - if (Stacks.isEmpty(big)) #small3(CommonState.norm(#copy(cur, aux, new, newN))) else #small2(cur, aux, Stacks.pop(big), ?(Stacks.unsafeFirst(big), new), 1 + newN) - }; - case (#small3(common)) #small3(CommonState.step(common)) - }; - - public func size(state : SmallState) : Nat = switch state { - case (#small1(cur, _, _)) Current.size(cur); - case (#small2(cur, _, _, _, _)) Current.size(cur); - case (#small3(common)) CommonState.size(common) - }; - - public func current(state : SmallState) : Current = switch state { - case (#small1(cur, _, _)) cur; - case (#small2(cur, _, _, _, _)) cur; - case (#small3(common)) CommonState.current(common) - } - }; - - type CopyState = { #copy : (Current, List, List, Nat) }; - - /// Represents the last rebalancing phase of both small and big ends of the queue. It is used to reverse the elements from the previous phases to restore the original order. It can be in one of the following states: - /// - /// - `#copy(cur, aux, new, sizeOfNew)`: Puts the elements from `aux` in reversed order on top of `new`. `#copy(cur, xn .. x1, new, sizeOfNew) ->* #copy(cur, [], x1 .. xn : new, n + sizeOfNew)`. - /// - `#idle(cur, idle)`: The rebalancing process is done and the queue is in the idle state. - type CommonState = CopyState or { #idle : (Current, Idle) }; - - module CommonState { - public func step(common : CommonState) : CommonState = switch common { - case (#copy copy) { - let (cur, aux, new, sizeOfNew) = copy; - let (_, _, _, targetSize) = cur; - norm(if (sizeOfNew < targetSize) #copy(cur, unsafeTail(aux), ?(unsafeHead(aux), new), 1 + sizeOfNew) else #copy copy) - }; - case (#idle _) common - }; - - public func norm(copy : CopyState) : CommonState { - let #copy(cur, _, new, sizeOfNew) = copy; - let (extra, extraSize, _, targetSize) = cur; - debug assert sizeOfNew <= targetSize; - if (sizeOfNew >= targetSize) { - #idle(cur, ((extra, new), extraSize + sizeOfNew)) // note: aux can be non-empty, thus ignored here, when the target size decreases after pop operations - } else copy - }; - - public func push(common : CommonState, t : T) : CommonState = switch common { - case (#copy(cur, aux, new, sizeOfNew)) #copy(Current.push(cur, t), aux, new, sizeOfNew); - case (#idle(cur, idle)) #idle(Current.push(cur, t), Idle.push(idle, t)) // yes, push to both - }; - - public func pop(common : CommonState) : (T, CommonState) = switch common { - case (#copy(cur, aux, new, sizeOfNew)) { - let (t, cur2) = Current.pop(cur); - (t, norm(#copy(cur2, aux, new, sizeOfNew))) - }; - case (#idle(cur, idle)) { - let (t, idle2) = Idle.pop(idle); - (t, #idle(Current.pop(cur).1, idle2)) - } - }; - - public func peek(common : CommonState) : T = switch common { - case (#copy(cur, _, _, _)) Current.peek(cur); - case (#idle(_, idle)) Idle.peek(idle) - }; - - public func size(common : CommonState) : Nat = switch common { - case (#copy(cur, _, _, _)) Current.size(cur); - case (#idle(_, (_, size))) size - }; - - public func current(common : CommonState) : Current = switch common { - case (#copy(cur, _, _, _)) cur; - case (#idle(cur, _)) cur - } - }; - - type States = ( - direction : Direction, - bigState : BigState, - smallState : SmallState - ); - - module States { - public func step(states : States) : States = switch states { - case (dir, #big1(_, bigTail, _, 0), #small1(currentS, _, auxS)) { - (dir, BigState.step(states.1), #small2(currentS, auxS, bigTail, null, 0)) - }; - case (dir, big, small) (dir, BigState.step(big), SmallState.step(small)) - } - }; - - type Direction = { #left; #right }; - - func idlesInvariant(((l, nL), (r, nR)) : (Idle, Idle)) : Bool = Stacks.size(l) == nL and Stacks.size(r) == nR and 3 * nL >= nR and 3 * nR >= nL; - - type List = Types.Pure.List; - type Iter = Types.Iter; - func unsafeHead(l : List) : T = Option.unwrap(l).0; - func unsafeTail(l : List) : List = Option.unwrap(l).1 -} diff --git a/.mops/core@2.3.1/src/pure/Set.mo b/.mops/core@2.3.1/src/pure/Set.mo deleted file mode 100644 index 020c79a..0000000 --- a/.mops/core@2.3.1/src/pure/Set.mo +++ /dev/null @@ -1,1563 +0,0 @@ -/// Pure (immutable) sets based on order/comparison of elements. -/// A set is a collection of elements without duplicates. -/// The set data structure type is stable and can be used for orthogonal persistence. -/// -/// Example: -/// ```motoko -/// import Set "mo:core/pure/Set"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// let set = Set.fromIter([3, 1, 2, 3].values(), Nat.compare); -/// assert Set.size(set) == 3; -/// assert not Set.contains(set, Nat.compare, 4); -/// let diff = Set.difference(set, set, Nat.compare); -/// assert Set.isEmpty(diff); -/// } -/// ``` -/// -/// These sets are implemented as red-black trees, a balanced binary search tree of ordered elements. -/// -/// The tree data structure internally colors each of its nodes either red or black, -/// and uses this information to balance the tree during modifying operations. -/// -/// Performance: -/// * Runtime: `O(log(n))` worst case cost per insertion, removal, and retrieval operation. -/// * Space: `O(n)` for storing the entire tree. -/// `n` denotes the number of elements (i.e. nodes) stored in the tree. -/// -/// Credits: -/// -/// The core of this implementation is derived from: -/// -/// * Ken Friis Larsen's [RedBlackMap.sml](https://github.com/kfl/mosml/blob/master/src/mosmllib/Redblackmap.sml), which itself is based on: -/// * Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](http://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html). - -import Runtime "../Runtime"; -import List "../List"; // NB: imperative! -import Iter "../Iter"; -import Types "../Types"; -import Nat "../Nat"; -import Order "../Order"; - -module { - - /// Ordered collection of unique elements of the generic type `T`. - /// If type `T` is stable then `Set` is also stable. - /// To ensure that property the `Set` does not have any methods, - /// instead they are gathered in the functor-like class `Operations` (see example there). - - /// @deprecated M0235 - public type Set = Types.Pure.Set; - - /// Red-black tree of nodes with ordered set elements. - /// Leaves are considered implicitly black. - type Tree = Types.Pure.Set.Tree; - - /// Create a set with the elements obtained from an iterator. - /// Potential duplicate elements in the iterator are ignored, i.e. - /// multiple occurrences of an equal element only occur once in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([3, 1, 2, 1].values(), Nat.compare); - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func fromIter(iter : Iter.Iter, compare : (implicit : (T, T) -> Order.Order)) : Set { - var set = empty() : Set; - for (val in iter) { - set := Internal.add(set, compare, val) - }; - set - }; - - /// Convert an iterator into a set. - /// Potential duplicate elements in the iterator are ignored, i.e. - /// multiple occurrences of an equal element only occur once in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = [3, 1, 2, 1].values(); - /// - /// let set = iter.toSet(Nat.compare); - /// - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func toSet(self : Iter.Iter, compare : (implicit : (T, T) -> Order.Order)) : Set { - fromIter(self, compare) - }; - - /// Given a `set` ordered by `compare`, insert the new `element`, - /// returning the new set. - /// - /// Return the set unchanged if the element already exists in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set0 = Set.empty(); - /// let set1 = Set.add(set0, Nat.compare, 2); - /// let set2 = Set.add(set1, Nat.compare, 1); - /// let set3 = Set.add(set2, Nat.compare, 2); - /// assert Iter.toArray(Set.values(set0)) == []; - /// assert Iter.toArray(Set.values(set1)) == [2]; - /// assert Iter.toArray(Set.values(set2)) == [1, 2]; - /// assert Iter.toArray(Set.values(set3)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned set shares with the `set` most of the tree nodes. - /// Garbage collecting one of the sets (e.g. after an assignment `m := Set.add(m, c, e)`) - /// causes collecting `O(log(n))` nodes. - public func add(self : Set, compare : (implicit : (T, T) -> Order.Order), elem : T) : Set = Internal.add(self, compare, elem); - - /// Given `set` ordered by `compare`, insert the new `element`, - /// returning the set extended with `element` and a Boolean indicating - /// if the element was already present in `set`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set0 = Set.empty(); - /// do { - /// let (set1, new1) = Set.insert(set0, Nat.compare, 2); - /// assert new1; - /// let (set2, new2) = Set.insert(set1, Nat.compare, 1); - /// assert new2; - /// let (set3, new3) = Set.insert(set2, Nat.compare, 2); - /// assert not new3; - /// assert Iter.toArray(Set.values(set3)) == [1, 2] - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned set shares with the `set` most of the tree nodes. - /// Garbage collecting one of the sets (e.g. after an assignment `m := Set.add(m, c, e)`) - /// causes collecting `O(log(n))` nodes. - public func insert(self : Set, compare : (implicit : (T, T) -> Order.Order), elem : T) : (Set, Bool) = Internal.insert(self, compare, elem); - - /// Given `set` ordered by `compare` return the set with `element` removed. - /// Return the set unchanged if the element was absent. - /// - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// - /// let set1 = Set.remove(set, Nat.compare, 2); - /// let set2 = Set.remove(set1, Nat.compare, 4); - /// assert Iter.toArray(Set.values(set2)) == [1, 3]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// Note: The returned set shares with `set` most of the tree nodes. - /// Garbage collecting one of the sets (e.g. after an assignment `m := Set.delete(m, c, e)`) - /// causes collecting `O(log(n))` nodes. - public func remove(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Set = Internal.remove(self, compare, element); - - /// Given `set` ordered by `compare`, delete `element` from the set, returning - /// either the set without the element and a Boolean indicating whether - /// whether `element` was contained in `set`. - /// - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// do { - /// let (set1, contained1) = Set.delete(set, Nat.compare, 2); - /// assert contained1; - /// assert Iter.toArray(Set.values(set1)) == [1, 3]; - /// let (set2, contained2) = Set.delete(set1, Nat.compare, 4); - /// assert not contained2; - /// assert Iter.toArray(Set.values(set2)) == [1, 3]; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// Note: The returned set shares with `set` most of the tree nodes. - /// Garbage collecting one of the sets (e.g. after an assignment `m := Set.delete(m, c, e)`) - /// causes collecting `O(log(n))` nodes. - public func delete(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : (Set, Bool) = Internal.delete(self, compare, element); - - /// Tests whether the set contains the provided element. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Bool "mo:core/Bool"; - /// - /// persistent actor { - /// let set = Set.fromIter([3, 1, 2].values(), Nat.compare); - /// - /// assert Set.contains(set, Nat.compare, 1); - /// assert not Set.contains(set, Nat.compare, 4); - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func contains(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Bool = Internal.contains(self.root, compare, element); - - /// Get the maximal element of the set `set` if it is not empty, otherwise returns `null` - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 2, 1].values(), Nat.compare); - /// let set2 = Set.empty(); - /// assert Set.max(set1) == ?2; - /// assert Set.max(set2) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements in the set - public func max(self : Set) : ?T = Internal.max(self.root); - - /// Retrieves the minimum element from the set. - /// If the set is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([2, 0, 1].values(), Nat.compare); - /// let set2 = Set.empty(); - /// assert Set.min(set1) == ?0; - /// assert Set.min(set2) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set. - public func min(self : Set) : ?T = Internal.min(self.root); - - /// Returns a new set that is the union of `set1` and `set2`, - /// i.e. a new set that all the elements that exist in at least on of the two sets. - /// Potential duplicates are ignored, i.e. if the same element occurs in both `set1` - /// and `set2`, it only occurs once in the returned set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let union = Set.union(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(union)) == [1, 2, 3, 4, 5]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(m)`, retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements in the sets, and `m <= n`. - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(m * log(n))` temporary objects that will be collected as garbage. - public func union(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - if (size(self) < size(other)) { - foldLeft(self, other, func(acc : Set, elem : T) : Set { Internal.add(acc, compare, elem) }) - } else { - foldLeft(other, self, func(acc : Set, elem : T) : Set { Internal.add(acc, compare, elem) }) - } - }; - - /// Returns a new set that is the intersection of `set1` and `set2`, - /// i.e. a new set that contains all the elements that exist in both sets. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let intersection = Set.intersection(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(intersection)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(m)` temporary objects that will be collected as garbage. - public func intersection(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - let elems = List.empty(); - if (self.size < other.size) { - Internal.iterate( - self.root, - func(x : T) { - if (Internal.contains(other.root, compare, x)) { - List.add(elems, x) - } - } - ) - } else { - Internal.iterate( - other.root, - func(x : T) { - if (Internal.contains(self.root, compare, x)) { - List.add(elems, x) - } - } - ) - }; - { root = Internal.buildFromSorted(elems); size = List.size(elems) } - }; - - /// Returns a new set that is the difference between `set1` and `other` (`set1` minus `set2`), - /// i.e. a new set that contains all the elements of `set1` that do not exist in `set2`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let difference = Set.difference(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(difference)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(m * log(n))` temporary objects that will be collected as garbage. - public func difference(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - if (size(self) < size(other)) { - let elems = List.empty(); /* imperative! */ - Internal.iterate( - self.root, - func(x : T) { - if (not Internal.contains(other.root, compare, x)) { - List.add(elems, x) - } - } - ); - { root = Internal.buildFromSorted(elems); size = List.size(elems) } - } else { - foldLeft( - other, - self, - func(acc : Set, elem : T) : Set { - if (Internal.contains(acc.root, compare, elem)) { - Internal.remove(acc, compare, elem) - } else { acc } - } - ) - } - }; - - /// Project all elements of the set in a new set. - /// Apply a mapping function to each element in the set and - /// collect the mapped elements in a new mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([3, 1, 2].values(), Nat.compare); - /// - /// let textNumbers = - /// Set.map(numbers, Text.compare, Nat.toText); - /// assert Iter.toArray(Set.values(textNumbers)) == ["1", "2", "3"]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func map(self : Set, compare : (implicit : (T2, T2) -> Order.Order), project : T1 -> T2) : Set = Internal.foldLeft(self.root, empty(), func(acc : Set, elem : T1) : Set { Internal.add(acc, compare, project(elem)) }); - - /// Apply an operation on each element contained in the set. - /// The operation is applied in ascending order of the elements. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// var text = ""; - /// Set.forEach(numbers, func (element) { - /// text #= " " # Nat.toText(element) - /// }); - /// assert text == " 0 1 2 3"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory. - /// where `n` denotes the number of elements stored in the set. - /// - public func forEach(self : Set, operation : T -> ()) { - ignore foldLeft(self, null, func(acc, e) : Null { operation(e); null }) - }; - - /// Filter elements in a new set. - /// Create a copy of the mutable set that only contains the elements - /// that fulfil the criterion function. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let evenNumbers = Set.filter(numbers, Nat.compare, func (number) { - /// number % 2 == 0 - /// }); - /// assert Iter.toArray(Set.values(evenNumbers)) == [0, 2]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func filter(self : Set, compare : (implicit : (T, T) -> Order.Order), criterion : T -> Bool) : Set { - foldLeft>( - self, - empty(), - func(acc, e) { - if (criterion(e)) (add(acc, compare, e)) else acc - } - ) - }; - - /// Filter all elements in the set by also applying a projection to the elements. - /// Apply a mapping function `project` to all elements in the set and collect all - /// elements, for which the function returns a non-null new element. Collect all - /// non-discarded new elements in a new mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([3, 0, 2, 1].values(), Nat.compare); - /// - /// let evenTextNumbers = Set.filterMap(numbers, Text.compare, func (number) { - /// if (number % 2 == 0) { - /// ?Nat.toText(number) - /// } else { - /// null // discard odd numbers - /// } - /// }); - /// assert Iter.toArray(Set.values(evenTextNumbers)) == ["0", "2"]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func filterMap(self : Set, compare : (implicit : (T2, T2) -> Order.Order), project : T1 -> ?T2) : Set { - func combine(acc : Set, elem : T1) : Set { - switch (project(elem)) { - case null { acc }; - case (?elem2) { - Internal.add(acc, compare, elem2) - } - } - }; - Internal.foldLeft(self.root, empty(), combine) - }; - - /// Test whether `set1` is a sub-set of `set2`, i.e. each element in `set1` is - /// also contained in `set2`. Returns `true` if both sets are equal. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([2, 1, 0].values(), Nat.compare); - /// let set3 = Set.fromIter([3, 4].values(), Nat.compare); - /// assert Set.isSubset(set1, set2, Nat.compare); - /// assert not Set.isSubset(set1, set3, Nat.compare); - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets set1 and set2, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func isSubset(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Bool { - if (self.size > other.size) { return false }; - isSubsetHelper(self.root, other.root, compare) - }; - - /// Test whether two sets are equal. - /// Both sets have to be constructed by the same comparison function. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([2, 1].values(), Nat.compare); - /// let set3 = Set.fromIter([2, 1, 0].values(), Nat.compare); - /// assert Set.equal(set1, set2, Nat.compare); - /// assert not Set.equal(set1, set3, Nat.compare); - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets set1 and set2, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func equal(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Bool { - if (self.size != other.size) { return false }; - isSubsetHelper(self.root, other.root, compare) - }; - - func isSubsetHelper(t1 : Tree, t2 : Tree, compare : (T, T) -> Order.Order) : Bool { - switch (t1, t2) { - case (#leaf, _) { true }; - case (_, #leaf) { false }; - case ((#red(t1l, x1, t1r) or #black(t1l, x1, t1r)), (#red(t2l, x2, t2r)) or #black(t2l, x2, t2r)) { - switch (compare(x1, x2)) { - case (#equal) { - isSubsetHelper(t1l, t2l, compare) and isSubsetHelper(t1r, t2r, compare) - }; - // x1 < x2 ==> x1 \in t2l /\ t1l \subset t2l - case (#less) { - Internal.contains(t2l, compare, x1) and isSubsetHelper(t1l, t2l, compare) and isSubsetHelper(t1r, t2, compare) - }; - // x2 < x1 ==> x1 \in t2r /\ t1r \subset t2r - case (#greater) { - Internal.contains(t2r, compare, x1) and isSubsetHelper(t1l, t2, compare) and isSubsetHelper(t1r, t2r, compare) - } - } - } - } - }; - - /// Compare two sets by comparing the elements. - /// Both sets must have been created by the same comparison function. - /// The two sets are iterated by the ascending order of their creation and - /// order is determined by the following rules: - /// Less: - /// `set1` is less than `set2` if: - /// * the pairwise iteration hits an element pair `element1` and `element2` where - /// `element1` is less than `element2` and all preceding elements are equal, or, - /// * `set1` is a strict prefix of `set2`, i.e. `set2` has more elements than `set1` - /// and all elements of `set1` occur at the beginning of iteration `set2`. - /// Equal: - /// `set1` and `set2` have same series of equal elements by pairwise iteration. - /// Greater: - /// `set1` is neither less nor equal `set2`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 1].values(), Nat.compare); - /// let set2 = Set.fromIter([0, 2].values(), Nat.compare); - /// - /// assert Set.compare(set1, set2, Nat.compare) == #less; - /// assert Set.compare(set1, set1, Nat.compare) == #equal; - /// assert Set.compare(set2, set1, Nat.compare) == #greater; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that `compare` has runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func compare(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - // TODO: optimize using recursion on self? - let iterator1 = values(self); - let iterator2 = values(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?element1, ?element2) { - let comparison = compare(element1, element2); - if (comparison != #equal) { - return comparison - } - } - } - } - }; - - /// Returns an iterator over the elements in the set, - /// traversing the elements in the ascending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 3, 1].values(), Nat.compare); - /// - /// var text = ""; - /// for (number in Set.values(set)) { - /// text #= " " # Nat.toText(number); - /// }; - /// assert text == " 0 1 2 3"; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func values(self : Set) : Iter.Iter = Internal.iter(self.root, #fwd); - - /// Returns an iterator over the elements in the set, - /// traversing the elements in the descending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 3, 1].values(), Nat.compare); - /// - /// var tmp = ""; - /// for (number in Set.reverseValues(set)) { - /// tmp #= " " # Nat.toText(number); - /// }; - /// assert tmp == " 3 2 1 0"; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseValues(self : Set) : Iter.Iter = Internal.iter(self.root, #bwd); - - /// Create a new empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Iter.toArray(Set.values(set)) == []; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Set = { root = #leaf; size = 0 }; - - /// Create a new set with a single element. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.singleton(0); - /// assert Iter.toArray(Set.values(set)) == [0]; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func singleton(element : T) : Set { - { - size = 1; - root = #red(#leaf, element, #leaf) - } - }; - - /// Return the number of elements in a set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1, 3].values(), Nat.compare); - /// - /// assert Set.size(set) == 4; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func size(self : Set) : Nat = self.size; - - /// Iterate all elements in ascending order, - /// and accumulate the elements by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1].values(), Nat.compare); - /// - /// let text = Set.foldLeft( - /// set, - /// "", - /// func (accumulator, element) { - /// accumulator # " " # Nat.toText(element) - /// } - /// ); - /// assert text == " 0 1 2 3"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - public func foldLeft( - self : Set, - base : A, - combine : (A, T) -> A - ) : A = Internal.foldLeft(self.root, base, combine); - - /// Iterate all elements in descending order, - /// and accumulate the elements by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1].values(), Nat.compare); - /// - /// let text = Set.foldRight( - /// set, - /// "", - /// func (element, accumulator) { - /// accumulator # " " # Nat.toText(element) - /// } - /// ); - /// assert text == " 3 2 1 0"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - public func foldRight( - self : Set, - base : A, - combine : (T, A) -> A - ) : A = Internal.foldRight(self.root, base, combine); - - /// Determines whether a set is empty. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.empty(); - /// let set2 = Set.singleton(1); - /// - /// assert Set.isEmpty(set1); - /// assert not Set.isEmpty(set2); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func isEmpty(self : Set) : Bool { - switch (self.root) { - case (#leaf) { true }; - case _ { false } - } - }; - - /// Check whether all element in the set satisfy a predicate, i.e. - /// the `predicate` function returns `true` for all elements in the set. - /// Returns `true` for an empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let belowTen = Set.all(set, func (number) { - /// number < 10 - /// }); - /// assert belowTen; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set. - public func all(self : Set, predicate : T -> Bool) : Bool = Internal.all(self.root, predicate); - - /// Check whether at least one element in the set satisfies a predicate, i.e. - /// the `predicate` function returns `true` for at least one element in the set. - /// Returns `false` for an empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let aboveTen = Set.any(set, func (number) { - /// number > 10 - /// }); - /// assert not aboveTen; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func any(self : Set, pred : T -> Bool) : Bool = Internal.any(self.root, pred); - - /// Test helper that check internal invariant for the given set `s`. - /// Raise an error (for a stack trace) if invariants are violated. - public func assertValid(self : Set, compare : (implicit : (T, T) -> Order.Order)) : () { - Internal.assertValid(self, compare) - }; - - /// Generate a textual representation of all the elements in the set. - /// Primarily to be used for testing and debugging. - /// The elements are formatted according to `elementFormat`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// assert Set.toText(set, Nat.toText) == "PureSet{0, 1, 2, 3}"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that `elementFormat` has runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func toText(self : Set, elementFormat : (implicit : (toText : T -> Text))) : Text { - var text = "PureSet{"; - var sep = ""; - for (element in values(self)) { - text #= sep # elementFormat(element); - sep := ", " - }; - text # "}" - }; - - /// Construct the union of a set of element sets, i.e. all elements of - /// each element set are included in the result set. - /// Any duplicates are ignored, i.e. if the same element occurs in multiple element sets, - /// it only occurs once in the result set. - /// - /// Assumes all sets are ordered by `compare`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Order "mo:core/Order"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// func setCompare(first: Set.Set, second: Set.Set) : Order.Order { - /// Set.compare(first, second, Nat.compare) - /// }; - /// - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let set3 = Set.fromIter([5, 6, 7].values(), Nat.compare); - /// let setOfSets = Set.fromIter([set1, set2, set3].values(), setCompare); - /// let flatSet = Set.flatten(setOfSets, Nat.compare); - /// assert Iter.toArray(Set.values(flatSet)) == [1, 2, 3, 4, 5, 6, 7]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in all the sub-sets, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func flatten(self : Set>, compare : (implicit : (T, T) -> Order.Order)) : Set { - var result = empty(); - for (set in values(self)) { - result := union(result, set, compare) - }; - result - }; - - /// Construct the union of a series of sets, i.e. all elements of - /// each set are included in the result set. - /// Any duplicates are ignored, i.e. if an element occurs - /// in several of the iterated sets, it only occurs once in the result set. - /// - /// Assumes all sets are ordered by `compare`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let set3 = Set.fromIter([5, 6, 7].values(), Nat.compare); - /// let combined = Set.join([set1, set2, set3].values(), Nat.compare); - /// assert Iter.toArray(Set.values(combined)) == [1, 2, 3, 4, 5, 6, 7]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the iterated sets, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func join(self : Iter.Iter>, compare : (implicit : (T, T) -> Order.Order)) : Set { - var result = empty(); - for (set in self) { - result := union(result, set, compare) - }; - result - }; - - module Internal { - public func contains(tree : Tree, compare : (T, T) -> Order.Order, elem : T) : Bool { - func f(t : Tree, x : T) : Bool { - switch t { - case (#black(l, x1, r)) { - switch (compare(x, x1)) { - case (#less) { f(l, x) }; - case (#equal) { true }; - case (#greater) { f(r, x) } - } - }; - case (#red(l, x1, r)) { - switch (compare(x, x1)) { - case (#less) { f(l, x) }; - case (#equal) { true }; - case (#greater) { f(r, x) } - } - }; - case (#leaf) { false } - } - }; - f(tree, elem) - }; - - public func max(m : Tree) : ?V { - func rightmost(m : Tree) : V { - switch m { - case (#red(_, v, #leaf)) { v }; - case (#red(_, _, r)) { rightmost(r) }; - case (#black(_, v, #leaf)) { v }; - case (#black(_, _, r)) { rightmost(r) }; - case (#leaf) { Runtime.trap "pure/Set.max() impossible" } - } - }; - switch m { - case (#leaf) { null }; - case (_) { ?rightmost(m) } - } - }; - - public func min(m : Tree) : ?V { - func leftmost(m : Tree) : V { - switch m { - case (#red(#leaf, v, _)) { v }; - case (#red(l, _, _)) { leftmost(l) }; - case (#black(#leaf, v, _)) { v }; - case (#black(l, _, _)) { leftmost(l) }; - case (#leaf) { Runtime.trap "pure/Set.min() impossible" } - } - }; - switch m { - case (#leaf) { null }; - case (_) { ?leftmost(m) } - } - }; - - public func all(m : Tree, pred : V -> Bool) : Bool { - switch m { - case (#red(l, v, r)) { - pred(v) and all(l, pred) and all(r, pred) - }; - case (#black(l, v, r)) { - pred(v) and all(l, pred) and all(r, pred) - }; - case (#leaf) { true } - } - }; - - public func any(m : Tree, pred : V -> Bool) : Bool { - switch m { - case (#red(l, v, r)) { - pred(v) or any(l, pred) or any(r, pred) - }; - case (#black(l, v, r)) { - pred(v) or any(l, pred) or any(r, pred) - }; - case (#leaf) { false } - } - }; - - public func iterate(m : Tree, f : V -> ()) { - switch m { - case (#leaf) {}; - case (#black(l, v, r)) { iterate(l, f); f(v); iterate(r, f) }; - case (#red(l, v, r)) { iterate(l, f); f(v); iterate(r, f) } - } - }; - - // build tree from elements arr[l]..arr[r-1] - public func buildFromSorted(buf : List.List) : Tree { - var maxDepth = 0; - var maxSize = 1; - while (maxSize < List.size(buf)) { - maxDepth += 1; - maxSize += maxSize + 1 - }; - maxDepth := if (maxDepth == 0) { 1 } else { maxDepth }; // keep root black for 1 element tree - func buildFromSortedHelper(l : Nat, r : Nat, depth : Nat) : Tree { - if (l + 1 == r) { - if (depth == maxDepth) { - return #red(#leaf, List.at(buf, l), #leaf) - } else { - return #black(#leaf, List.at(buf, l), #leaf) - } - }; - if (l >= r) { - return #leaf - }; - let m = (l + r) / 2; - return #black( - buildFromSortedHelper(l, m, depth + 1), - List.at(buf, m), - buildFromSortedHelper(m + 1, r, depth + 1) - ) - }; - buildFromSortedHelper(0, List.size(buf), 0) - }; - - type IterRep = Types.Pure.List<{ #tr : Tree; #x : T }>; - - type SetTraverser = (Tree, T, Tree, IterRep) -> IterRep; - - class IterSet(tree : Tree, setTraverser : SetTraverser) { - var trees : IterRep = ?(#tr(tree), null); - public func next() : ?T { - switch (trees) { - case (null) { null }; - case (?(#tr(#leaf), ts)) { - trees := ts; - next() - }; - case (?(#x(x), ts)) { - trees := ts; - ?x - }; - case (?(#tr(#black(l, x, r)), ts)) { - trees := setTraverser(l, x, r, ts); - next() - }; - case (?(#tr(#red(l, x, r)), ts)) { - trees := setTraverser(l, x, r, ts); - next() - } - } - } - }; - - public func iter(s : Tree, direction : { #fwd; #bwd }) : Iter.Iter { - let turnLeftFirst : SetTraverser = func(l, x, r, ts) { - ?(#tr(l), ?(#x(x), ?(#tr(r), ts))) - }; - - let turnRightFirst : SetTraverser = func(l, x, r, ts) { - ?(#tr(r), ?(#x(x), ?(#tr(l), ts))) - }; - - switch direction { - case (#fwd) IterSet(s, turnLeftFirst); - case (#bwd) IterSet(s, turnRightFirst) - } - }; - - public func foldLeft( - tree : Tree, - base : Accum, - combine : (Accum, T) -> Accum - ) : Accum { - switch (tree) { - case (#leaf) { base }; - case (#black(l, x, r)) { - let left = foldLeft(l, base, combine); - let middle = combine(left, x); - foldLeft(r, middle, combine) - }; - case (#red(l, x, r)) { - let left = foldLeft(l, base, combine); - let middle = combine(left, x); - foldLeft(r, middle, combine) - } - } - }; - - public func foldRight( - tree : Tree, - base : Accum, - combine : (T, Accum) -> Accum - ) : Accum { - switch (tree) { - case (#leaf) { base }; - case (#black(l, x, r)) { - let right = foldRight(r, base, combine); - let middle = combine(x, right); - foldRight(l, middle, combine) - }; - case (#red(l, x, r)) { - let right = foldRight(r, base, combine); - let middle = combine(x, right); - foldRight(l, middle, combine) - } - } - }; - - func redden(t : Tree) : Tree { - switch t { - case (#black(l, x, r)) { (#red(l, x, r)) }; - case _ { - Runtime.trap "pure/Set.redden() impossible" - } - } - }; - - func lbalance(left : Tree, x : T, right : Tree) : Tree { - switch (left, right) { - case (#red(#red(l1, x1, r1), x2, r2), r) { - #red( - #black(l1, x1, r1), - x2, - #black(r2, x, r) - ) - }; - case (#red(l1, x1, #red(l2, x2, r2)), r) { - #red( - #black(l1, x1, l2), - x2, - #black(r2, x, r) - ) - }; - case _ { - #black(left, x, right) - } - } - }; - - func rbalance(left : Tree, x : T, right : Tree) : Tree { - switch (left, right) { - case (l, #red(l1, x1, #red(l2, x2, r2))) { - #red( - #black(l, x, l1), - x1, - #black(l2, x2, r2) - ) - }; - case (l, #red(#red(l1, x1, r1), x2, r2)) { - #red( - #black(l, x, l1), - x1, - #black(r1, x2, r2) - ) - }; - case _ { - #black(left, x, right) - } - } - }; - - public func add( - set : Set, - compare : (T, T) -> Order.Order, - elem : T - ) : Set { - insert(set, compare, elem).0 - }; - - public func insert( - s : Set, - compare : (T, T) -> Order.Order, - elem : T - ) : (Set, Bool) { - var newNodeIsCreated : Bool = false; - func ins(tree : Tree) : Tree { - switch tree { - case (#black(left, x, right)) { - switch (compare(elem, x)) { - case (#less) { - lbalance(ins left, x, right) - }; - case (#greater) { - rbalance(left, x, ins right) - }; - case (#equal) { - #black(left, x, right) - } - } - }; - case (#red(left, x, right)) { - switch (compare(elem, x)) { - case (#less) { - #red(ins left, x, right) - }; - case (#greater) { - #red(left, x, ins right) - }; - case (#equal) { - #red(left, x, right) - } - } - }; - case (#leaf) { - newNodeIsCreated := true; - #red(#leaf, elem, #leaf) - } - } - }; - let newRoot = switch (ins(s.root)) { - case (#red(left, x, right)) { - #black(left, x, right) - }; - case other { other } - }; - if newNodeIsCreated ({ root = newRoot; size = s.size + 1 }, true) else (s, false) - }; - - func balLeft(left : Tree, x : T, right : Tree) : Tree { - switch (left, right) { - case (#red(l1, x1, r1), r) { - #red(#black(l1, x1, r1), x, r) - }; - case (_, #black(l2, x2, r2)) { - rbalance(left, x, #red(l2, x2, r2)) - }; - case (_, #red(#black(l2, x2, r2), x3, r3)) { - #red( - #black(left, x, l2), - x2, - rbalance(r2, x3, redden r3) - ) - }; - case _ { Runtime.trap "pure/Set.balLeft() impossible" } - } - }; - - func balRight(left : Tree, x : T, right : Tree) : Tree { - switch (left, right) { - case (l, #red(l1, x1, r1)) { - #red(l, x, #black(l1, x1, r1)) - }; - case (#black(l1, x1, r1), r) { - lbalance(#red(l1, x1, r1), x, r) - }; - case (#red(l1, x1, #black(l2, x2, r2)), r3) { - #red( - lbalance(redden l1, x1, l2), - x2, - #black(r2, x, r3) - ) - }; - case _ { Runtime.trap "pure/Set.balRight() impossible" } - } - }; - - func append(left : Tree, right : Tree) : Tree { - switch (left, right) { - case (#leaf, _) { right }; - case (_, #leaf) { left }; - case ( - #red(l1, x1, r1), - #red(l2, x2, r2) - ) { - switch (append(r1, l2)) { - case (#red(l3, x3, r3)) { - #red( - #red(l1, x1, l3), - x3, - #red(r3, x2, r2) - ) - }; - case r1l2 { - #red(l1, x1, #red(r1l2, x2, r2)) - } - } - }; - case (t1, #red(l2, x2, r2)) { - #red(append(t1, l2), x2, r2) - }; - case (#red(l1, x1, r1), t2) { - #red(l1, x1, append(r1, t2)) - }; - case (#black(l1, x1, r1), #black(l2, x2, r2)) { - switch (append(r1, l2)) { - case (#red(l3, x3, r3)) { - #red( - #black(l1, x1, l3), - x3, - #black(r3, x2, r2) - ) - }; - case r1l2 { - balLeft( - l1, - x1, - #black(r1l2, x2, r2) - ) - } - } - } - } - }; - - public func remove(set : Set, compare : (T, T) -> Order.Order, elem : T) : Set { - delete(set, compare, elem).0 - }; - - public func delete(s : Set, compare : (T, T) -> Order.Order, x : T) : (Set, Bool) { - var changed : Bool = false; - func delNode(left : Tree, x1 : T, right : Tree) : Tree { - switch (compare(x, x1)) { - case (#less) { - let newLeft = del left; - switch left { - case (#black(_, _, _)) { - balLeft(newLeft, x1, right) - }; - case _ { - #red(newLeft, x1, right) - } - } - }; - case (#greater) { - let newRight = del right; - switch right { - case (#black(_, _, _)) { - balRight(left, x1, newRight) - }; - case _ { - #red(left, x1, newRight) - } - } - }; - case (#equal) { - changed := true; - append(left, right) - } - } - }; - func del(tree : Tree) : Tree { - switch tree { - case (#black(left, x1, right)) { - delNode(left, x1, right) - }; - case (#red(left, x1, right)) { - delNode(left, x1, right) - }; - case (#leaf) { - tree - } - } - }; - let newRoot = switch (del(s.root)) { - case (#red(left, x1, right)) { - #black(left, x1, right) - }; - case other { other } - }; - if changed ({ root = newRoot; size = s.size - 1 }, true) else (s, false) - }; - - // check binary search tree order of elements and black depth invariant of the RB-tree - public func assertValid(s : Set, comp : (T, T) -> Order.Order) { - ignore blackDepth(s.root, comp) - }; - - func blackDepth(node : Tree, comp : (T, T) -> Order.Order) : Nat { - func checkNode(left : Tree, x1 : T, right : Tree) : Nat { - checkElem(left, func(x : T) : Bool { comp(x, x1) == #less }); - checkElem(right, func(x : T) : Bool { comp(x, x1) == #greater }); - let leftBlacks = blackDepth(left, comp); - let rightBlacks = blackDepth(right, comp); - assert (leftBlacks == rightBlacks); - leftBlacks - }; - switch node { - case (#leaf) 0; - case (#red(left, x1, right)) { - assert (not isRed(left)); - assert (not isRed(right)); - checkNode(left, x1, right) - }; - case (#black(left, x1, right)) { - checkNode(left, x1, right) + 1 - } - } - }; - - func isRed(node : Tree) : Bool { - switch node { - case (#red(_, _, _)) true; - case _ false - } - }; - - func checkElem(node : Tree, isValid : T -> Bool) { - switch node { - case (#leaf) {}; - case (#black(_, elem, _)) { - assert (isValid(elem)) - }; - case (#red(_, elem, _)) { - assert (isValid(elem)) - } - } - } - }; - -} diff --git a/.mops/core@2.4.0/LICENSE b/.mops/core@2.4.0/LICENSE deleted file mode 100644 index f593a1f..0000000 --- a/.mops/core@2.4.0/LICENSE +++ /dev/null @@ -1,208 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, and - distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by the - copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all other - entities that control, are controlled by, or are under common control with - that entity. For the purposes of this definition, "control" means (i) the - power, direct or indirect, to cause the direction or management of such - entity, whether by contract or otherwise, or (ii) ownership of fifty percent - (50%) or more of the outstanding shares, or (iii) beneficial ownership of - such entity. - - "You" (or "Your") shall mean an individual or Legal Entity exercising - permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation source, and - configuration files. - - "Object" form shall mean any form resulting from mechanical transformation - or translation of a Source form, including but not limited to compiled - object code, generated documentation, and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or Object form, - made available under the License, as indicated by a copyright notice that is - included in or attached to the work (an example is provided in the Appendix - below). - - "Derivative Works" shall mean any work, whether in Source or Object form, - that is based on (or derived from) the Work and for which the editorial - revisions, annotations, elaborations, or other modifications represent, as a - whole, an original work of authorship. For the purposes of this License, - Derivative Works shall not include works that remain separable from, or - merely link (or bind by name) to the interfaces of, the Work and Derivative - Works thereof. - - "Contribution" shall mean any work of authorship, including the original - version of the Work and any modifications or additions to that Work or - Derivative Works thereof, that is intentionally submitted to Licensor for - inclusion in the Work by the copyright owner or by an individual or Legal - Entity authorized to submit on behalf of the copyright owner. For the - purposes of this definition, "submitted" means any form of electronic, - verbal, or written communication sent to the Licensor or its - representatives, including but not limited to communication on electronic - mailing lists, source code control systems, and issue tracking systems that - are managed by, or on behalf of, the Licensor for the purpose of discussing - and improving the Work, but excluding communication that is conspicuously - marked or otherwise designated in writing by the copyright owner as "Not a - Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity on - behalf of whom a Contribution has been received by Licensor and subsequently - incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this - License, each Contributor hereby grants to You a perpetual, worldwide, - non-exclusive, no-charge, royalty-free, irrevocable copyright license to - reproduce, prepare Derivative Works of, publicly display, publicly perform, - sublicense, and distribute the Work and such Derivative Works in Source or - Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this - License, each Contributor hereby grants to You a perpetual, worldwide, - non-exclusive, no-charge, royalty-free, irrevocable (except as stated in - this section) patent license to make, have made, use, offer to sell, sell, - import, and otherwise transfer the Work, where such license applies only to - those patent claims licensable by such Contributor that are necessarily - infringed by their Contribution(s) alone or by combination of their - Contribution(s) with the Work to which such Contribution(s) was submitted. - If You institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work or a - Contribution incorporated within the Work constitutes direct or contributory - patent infringement, then any patent licenses granted to You under this - License for that Work shall terminate as of the date such litigation is - filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or - Derivative Works thereof in any medium, with or without modifications, and - in Source or Object form, provided that You meet the following conditions: - - a. You must give any other recipients of the Work or Derivative Works a - copy of this License; and - - b. You must cause any modified files to carry prominent notices stating - that You changed the files; and - - c. You must retain, in the Source form of any Derivative Works that You - distribute, all copyright, patent, trademark, and attribution notices - from the Source form of the Work, excluding those notices that do not - pertain to any part of the Derivative Works; and - - d. If the Work includes a "NOTICE" text file as part of its distribution, - then any Derivative Works that You distribute must include a readable - copy of the attribution notices contained within such NOTICE file, - excluding those notices that do not pertain to any part of the Derivative - Works, in at least one of the following places: within a NOTICE text file - distributed as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, within a - display generated by the Derivative Works, if and wherever such - third-party notices normally appear. The contents of the NOTICE file are - for informational purposes only and do not modify the License. You may - add Your own attribution notices within Derivative Works that You - distribute, alongside or as an addendum to the NOTICE text from the Work, - provided that such additional attribution notices cannot be construed as - modifying the License. - - You may add Your own copyright statement to Your modifications and may - provide additional or different license terms and conditions for use, - reproduction, or distribution of Your modifications, or for any such - Derivative Works as a whole, provided Your use, reproduction, and - distribution of the Work otherwise complies with the conditions stated in - this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any - Contribution intentionally submitted for inclusion in the Work by You to the - Licensor shall be under the terms and conditions of this License, without - any additional terms or conditions. Notwithstanding the above, nothing - herein shall supersede or modify the terms of any separate license agreement - you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, - trademarks, service marks, or product names of the Licensor, except as - required for reasonable and customary use in describing the origin of the - Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in - writing, Licensor provides the Work (and each Contributor provides its - Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied, including, without limitation, any - warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or - FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining - the appropriateness of using or redistributing the Work and assume any risks - associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in - tort (including negligence), contract, or otherwise, unless required by - applicable law (such as deliberate and grossly negligent acts) or agreed to - in writing, shall any Contributor be liable to You for damages, including - any direct, indirect, special, incidental, or consequential damages of any - character arising as a result of this License or out of the use or inability - to use the Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all other - commercial damages or losses), even if such Contributor has been advised of - the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or - Derivative Works thereof, You may choose to offer, and charge a fee for, - acceptance of support, warranty, indemnity, or other liability obligations - and/or rights consistent with this License. However, in accepting such - obligations, You may act only on Your own behalf and on Your sole - responsibility, not on behalf of any other Contributor, and only if You - agree to indemnify, defend, and hold each Contributor harmless for any - liability incurred by, or claims asserted against, such Contributor by - reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -LLVM EXCEPTIONS TO THE APACHE 2.0 LICENSE - -As an exception, if, as a result of your compiling your source code, portions -of this Software are embedded into an Object form of such source code, you may -redistribute such embedded portions in such Object form without complying with -the conditions of Sections 4(a), 4(b) and 4(d) of the License. - -In addition, if you combine or link compiled forms of this Software with -software that is licensed under the GPLv2 ("Combined Software") and if a court -of competent jurisdiction determines that the patent provision (Section 3), the -indemnity provision (Section 9) or other Section of the License conflicts with -the conditions of the GPLv2, you may retroactively and prospectively choose to -deem waived or otherwise exclude such Section(s) of the License, but only in -their entirety and only with respect to the Combined Software. - -END OF LLVM EXCEPTIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification -within third-party archives. - -Copyright 2025 DFINITY Stiftung - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. - -END OF APPENDIX diff --git a/.mops/core@2.4.0/NOTICE b/.mops/core@2.4.0/NOTICE deleted file mode 100644 index a25e095..0000000 --- a/.mops/core@2.4.0/NOTICE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright 2025 DFINITY Stiftung - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. - -This product contains modified software originally developed by MR Research AG, -used with permission: - -* https://github.com/research-ag/vector -* https://github.com/research-ag/prng diff --git a/.mops/core@2.4.0/README.md b/.mops/core@2.4.0/README.md deleted file mode 100644 index 5319dad..0000000 --- a/.mops/core@2.4.0/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# `core` - -* 📦 [Mops Package](https://mops.one/core) -* ✨ [Documentation](https://internetcomputer.org/docs/motoko/core) - ---- - -The `core` package is the official standard library for the [Motoko](https://github.com/dfinity/motoko) programming language. - -This replaces the original `base` library, which is available [here](https://github.com/dfinity/motoko-base). - -An official [migration guide](https://internetcomputer.org/docs/motoko/base-core-migration) is available for upgrading projects from `base` to `core`. - -## Quick Start - -1. Install the [Mops](https://docs.mops.one/quick-start) package manager -2. Open a terminal in your project directory -3. Run `mops add core` - -This adds the following dependency to your `mops.toml` config file: - -```toml -[dependencies] -core = "2.4.0" -``` - -## Contributing - -This repository is currently closed to external contributions. Please feel free to report a bug, ask a question, or request a feature on the project's [GitHub issues](https://github.com/dfinity/motoko-core/issues) page. - -Interface design and code style guidelines for the repository can be found [here](https://github.com/dfinity/motoko-core/blob/main/Styleguide.md). - -### Dev Environment - -> Make sure that [Node.js](https://nodejs.org/en/) `>= 22.x` is installed on your system. - -Run the following commands to configure your local development branch: - -```sh -# First-time setup -git clone https://github.com/dfinity/motoko-core -cd motoko-core -npm ci -npx ic-mops toolchain init -``` - -Below is a quick reference for commonly-used scripts during development: - -```sh -npm test # Run all tests -npm run format # Format Motoko files -npm run validate:api # Update the public API lockfile -npm run validate:docs Array # Run code snippets in `src/Array.mo` -``` - -All available scripts can be found in the project's [`package.json`](https://github.com/dfinity/motoko-core/blob/main/package.json) file. - -### Major Contributors - -Big thanks to the following community contributors: - -* [MR Research AG (A. Stepanov, T. Hanke)](https://github.com/research-ag): [`vector`](https://github.com/research-ag/vector), [`prng`](https://github.com/research-ag/prng) -* [Byron Becker](https://github.com/ByronBecker): [`StableHeapBTreeMap`](https://github.com/canscale/StableHeapBTreeMap) -* [Zen Voich](https://github.com/ZenVoich): [`test`](https://github.com/ZenVoich/test) diff --git a/.mops/core@2.4.0/mops.toml b/.mops/core@2.4.0/mops.toml deleted file mode 100644 index 3cdfa84..0000000 --- a/.mops/core@2.4.0/mops.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "core" -version = "2.4.0" -description = "The Motoko standard library" -repository = "https://github.com/caffeinelabs/motoko-core" -keywords = [ - "core", - "base", - "data-structure", - "stable-memory", - "persistent" -] -license = "Apache-2.0" - -[dev-dependencies] -test = "2.1.1" -bench = "1.0.0" -fuzz = "1.0.0" -matchers = "2.1.0" -base-0-14-13 = "https://github.com/dfinity/motoko-base#moc-0.14.13@794174a307975c225cfb26b57f73e38a841c0415" -bench-helper = "0.0.3" - -[requirements] -moc = "1.4.1" - -[toolchain] -moc = "1.4.1" -wasmtime = "35.0.0" diff --git a/.mops/core@2.4.0/src/Array.mo b/.mops/core@2.4.0/src/Array.mo deleted file mode 100644 index d833ced..0000000 --- a/.mops/core@2.4.0/src/Array.mo +++ /dev/null @@ -1,1182 +0,0 @@ -/// Provides extended utility functions on immutable Arrays (values of type `[T]`). -/// -/// Note the difference between mutable (`[var T]`) and immutable (`[T]`) arrays. -/// Mutable arrays allow their elements to be modified after creation, while -/// immutable arrays are fixed once created. -/// -/// WARNING: If you are looking for a list that can grow and shrink in size, -/// it is recommended you use `List` for those purposes. -/// Arrays must be created with a fixed size. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Array "mo:core/Array"; -/// ``` - -import Order "Order"; -import VarArray "VarArray"; -import Option "Option"; -import Types "Types"; -import Prim "mo:⛔"; - -module { - - /// Creates an empty array (equivalent to `[]`). - /// - /// ```motoko include=import - /// let array = Array.empty(); - /// assert array == []; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func empty() : [T] = []; - - /// Creates an array containing `item` repeated `size` times. - /// - /// ```motoko include=import - /// let array = Array.repeat("Echo", 3); - /// assert array == ["Echo", "Echo", "Echo"]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func repeat(item : T, size : Nat) : [T] = Prim.Array_tabulate(size, func _ = item); - - /// Creates an immutable array of size `size`. Each element at index i - /// is created by applying `generator` to i. - /// - /// ```motoko include=import - /// let array : [Nat] = Array.tabulate(4, func i = i * 2); - /// assert array == [0, 2, 4, 6]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `generator` runs in O(1) time and space. - public let tabulate : (size : Nat, generator : Nat -> T) -> [T] = Prim.Array_tabulate; - - /// Transforms a mutable array into an immutable array. - /// - /// ```motoko include=import - /// let varArray = [var 0, 1, 2]; - /// varArray[2] := 3; - /// let array = Array.fromVarArray(varArray); - /// assert array == [0, 1, 3]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// @deprecated M0235 - public func fromVarArray(varArray : [var T]) : [T] = Prim.Array_tabulate(varArray.size(), func i = varArray[i]); - - /// Transforms an immutable array into a mutable array. - /// - /// ```motoko include=import - /// import VarArray "mo:core/VarArray"; - /// import Nat "mo:core/Nat"; - /// - /// let array = [0, 1, 2]; - /// let varArray = Array.toVarArray(array); - /// varArray[2] := 3; - /// assert VarArray.equal(varArray, [var 0, 1, 3], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func toVarArray(self : [T]) : [var T] { - let size = self.size(); - if (size == 0) { - return [var] - }; - let newArray = Prim.Array_init(size, self[0]); - var i = 0; - while (i < size) { - newArray[i] := self[i]; - i += 1 - }; - newArray - }; - - /// Tests if two arrays contain equal values (i.e. they represent the same - /// list of elements). Uses `equal` to compare elements in the arrays. - /// - /// ```motoko include=import - /// // Use the equal function from the Nat module to compare Nats - /// import {equal} "mo:core/Nat"; - /// - /// let array1 = [0, 1, 2, 3]; - /// let array2 = [0, 1, 2, 3]; - /// assert Array.equal(array1, array2, equal); - /// ``` - /// - /// Runtime: O(size1 + size2) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func equal(self : [T], other : [T], equal : (implicit : (T, T) -> Bool)) : Bool { - let size1 = self.size(); - let size2 = other.size(); - if (size1 != size2) { - return false - }; - var i = 0; - while (i < size1) { - if (not equal(self[i], other[i])) { - return false - }; - i += 1 - }; - true - }; - - /// Returns the first value in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let array = [1, 9, 4, 8]; - /// let found = Array.find(array, func x = x > 8); - /// assert found == ?9; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func find(self : [T], predicate : T -> Bool) : ?T { - for (element in self.vals()) { - if (predicate(element)) { - return ?element - } - }; - null - }; - - /// Returns the first index in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let array = ['A', 'B', 'C', 'D']; - /// let found = Array.findIndex(array, func(x) { x == 'C' }); - /// assert found == ?2; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func findIndex(self : [T], predicate : T -> Bool) : ?Nat { - for ((index, element) in enumerate(self)) { - if (predicate(element)) { - return ?index - } - }; - null - }; - - /// Create a new array by concatenating the values of `array1` and `array2`. - /// Note that `Array.concat` copies its arguments and has linear complexity. - /// - /// ```motoko include=import - /// let array1 = [1, 2, 3]; - /// let array2 = [4, 5, 6]; - /// let result = Array.concat(array1, array2); - /// assert result == [1, 2, 3, 4, 5, 6]; - /// ``` - /// Runtime: O(size1 + size2) - /// - /// Space: O(size1 + size2) - public func concat(self : [T], other : [T]) : [T] { - let size1 = self.size(); - let size2 = other.size(); - Prim.Array_tabulate( - size1 + size2, - func i { - if (i < size1) { - self[i] - } else { - other[i - size1] - } - } - ) - }; - - /// Sorts the elements in the array according to `compare`. - /// Sort is deterministic and stable. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [4, 2, 6]; - /// let sorted = Array.sort(array, Nat.compare); - /// assert sorted == [2, 4, 6]; - /// ``` - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sort(self : [T], compare : (implicit : (T, T) -> Order.Order)) : [T] { - let varArray : [var T] = toVarArray(self); - VarArray.sortInPlace(varArray, compare); - fromVarArray(varArray) - }; - - /// Creates a new array by reversing the order of elements in `array`. - /// - /// ```motoko include=import - /// let array = [10, 11, 12]; - /// let reversed = Array.reverse(array); - /// assert reversed == [12, 11, 10]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func reverse(self : [T]) : [T] { - let size = self.size(); - Prim.Array_tabulate(size, func i = self[size - i - 1]) - }; - - /// Calls `f` with each element in `array`. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// var sum = 0; - /// let array = [0, 1, 2, 3]; - /// Array.forEach(array, func(x) { - /// sum += x; - /// }); - /// assert sum == 6; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEach(self : [T], f : T -> ()) { - for (item in self.vals()) { - f(item) - } - }; - - /// Creates a new array by applying `f` to each element in `array`. `f` "maps" - /// each element it is applied to of type `X` to an element of type `Y`. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// let array1 = [0, 1, 2, 3]; - /// let array2 = Array.map(array1, func x = x * 2); - /// assert array2 == [0, 2, 4, 6]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func map(self : [T], f : T -> R) : [R] = Prim.Array_tabulate(self.size(), func i = f(self[i])); - - /// Creates a new array by applying `predicate` to every element - /// in `array`, retaining the elements for which `predicate` returns true. - /// - /// ```motoko include=import - /// let array = [4, 2, 6, 1, 5]; - /// let evenElements = Array.filter(array, func x = x % 2 == 0); - /// assert evenElements == [4, 2, 6]; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func filter(self : [T], f : T -> Bool) : [T] { - var count = 0; - let keep = Prim.Array_tabulate( - self.size(), - func i { - if (f(self[i])) { - count += 1; - true - } else { - false - } - } - ); - var nextKeep = 0; - Prim.Array_tabulate( - count, - func _ { - while (not keep[nextKeep]) { - nextKeep += 1 - }; - nextKeep += 1; - self[nextKeep - 1] - } - ) - }; - - /// Creates a new array by applying `f` to each element in `array`, - /// and keeping all non-null elements. The ordering is retained. - /// - /// ```motoko include=import - /// import {toText} "mo:core/Nat"; - /// - /// let array = [4, 2, 0, 1]; - /// let newArray = - /// Array.filterMap( // mapping from Nat to Text values - /// array, - /// func x = if (x == 0) { null } else { ?toText(100 / x) } // can't divide by 0, so return null - /// ); - /// assert newArray == ["25", "50", "100"]; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func filterMap(self : [T], f : T -> ?R) : [R] { - var count = 0; - let options = Prim.Array_tabulate( - self.size(), - func i { - let result = f(self[i]); - switch (result) { - case (?element) { - count += 1; - result - }; - case null { - null - } - } - } - ); - - var nextSome = 0; - Prim.Array_tabulate( - count, - func _ { - while (Option.isNull(options[nextSome])) { - nextSome += 1 - }; - nextSome += 1; - switch (options[nextSome - 1]) { - case (?element) element; - case null { - Prim.trap "Array.filterMap(): malformed array" - } - } - } - ) - }; - - /// Creates a new array by applying `f` to each element in `array`. - /// If any invocation of `f` produces an `#err`, returns an `#err`. Otherwise - /// returns an `#ok` containing the new array. - /// - /// ```motoko include=import - /// let array = [4, 3, 2, 1, 0]; - /// // divide 100 by every element in the array - /// let result = Array.mapResult(array, func x { - /// if (x > 0) { - /// #ok(100 / x) - /// } else { - /// #err "Cannot divide by zero" - /// } - /// }); - /// assert result == #err "Cannot divide by zero"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - /// @deprecated M0235 - public func mapResult(self : [T], f : T -> Types.Result) : Types.Result<[R], E> { - let size = self.size(); - - var error : ?Types.Result<[R], E> = null; - let results = Prim.Array_tabulate( - size, - func i { - switch (f(self[i])) { - case (#ok element) { - ?element - }; - case (#err e) { - switch (error) { - case null { - // only take the first error - error := ?(#err e) - }; - case _ {} - }; - null - } - } - } - ); - - switch error { - case null { - // unpack the option - #ok( - map( - results, - func element { - switch element { - case (?element) { - element - }; - case null { - Prim.trap "Array.mapResult(): malformed array" - } - } - } - ) - ) - }; - case (?error) { - error - } - } - }; - - /// Creates a new array by applying `f` to each element in `array` and its index. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// let array = [10, 10, 10, 10]; - /// let newArray = Array.mapEntries(array, func (x, i) = i * x); - /// assert newArray == [0, 10, 20, 30]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapEntries(self : [T], f : (T, Nat) -> R) : [R] = Prim.Array_tabulate(self.size(), func i = f(self[i], i)); - - /// Creates a new array by applying `k` to each element in `array`, - /// and concatenating the resulting arrays in order. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4]; - /// let newArray = Array.flatMap(array, func x = [x, -x].values()); - /// assert newArray == [1, -1, 2, -2, 3, -3, 4, -4]; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `k` runs in O(1) time and space. - public func flatMap(self : [T], k : T -> Types.Iter) : [R] { - var flatSize = 0; - let arrays = Prim.Array_tabulate<[R]>( - self.size(), - func i { - let subArray = fromIter(k(self[i])); - flatSize += subArray.size(); - subArray - } - ); - - // could replace with a call to flatten, - // but it would require an extra pass (to compute `flatSize`) - var outer = 0; - var inner = 0; - Prim.Array_tabulate( - flatSize, - func _ { - while (inner == arrays[outer].size()) { - inner := 0; - outer += 1 - }; - let element = arrays[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// ```motoko include=import - /// import {add} "mo:core/Nat"; - /// - /// let array = [4, 2, 0, 1]; - /// let sum = - /// Array.foldLeft( - /// array, - /// 0, // start the sum at 0 - /// func(sumSoFar, x) = sumSoFar + x // this entire function can be replaced with `add`! - /// ); - /// assert sum == 7; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(self : [T], base : A, combine : (A, T) -> A) : A { - var acc = base; - for (element in self.values()) { - acc := combine(acc, element) - }; - acc - }; - - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// ```motoko include=import - /// import {toText} "mo:core/Nat"; - /// - /// let array = [1, 9, 4, 8]; - /// let bookTitle = Array.foldRight(array, "", func(x, acc) = toText(x) # acc); - /// assert bookTitle == "1948"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldRight(self : [T], base : A, combine : (T, A) -> A) : A { - var acc = base; - let size = self.size(); - var i = size; - while (i > 0) { - i -= 1; - acc := combine(self[i], acc) - }; - acc - }; - - /// Combines an iterator of arrays into a single array. Retains the original - /// ordering of the elements. - /// - /// Consider using `Array.flatten()` for better performance. - /// - /// ```motoko include=import - /// let arrays = [[0, 1, 2], [2, 3], [], [4]]; - /// let joinedArray = Array.join(arrays.values()); - /// assert joinedArray == [0, 1, 2, 2, 3, 4]; - /// ``` - /// - /// Runtime: O(number of elements in array) - /// - /// Space: O(number of elements in array) - public func join(self : Types.Iter<[T]>) : [T] { - flatten(fromIter(self)) - }; - - /// Combines an array of arrays into a single array. Retains the original - /// ordering of the elements. - /// - /// This has better performance compared to `Array.join()`. - /// - /// ```motoko include=import - /// let arrays = [[0, 1, 2], [2, 3], [], [4]]; - /// let flatArray = Array.flatten(arrays); - /// assert flatArray == [0, 1, 2, 2, 3, 4]; - /// ``` - /// - /// Runtime: O(number of elements in array) - /// - /// Space: O(number of elements in array) - public func flatten(self : [[T]]) : [T] { - var flatSize = 0; - for (subArray in self.vals()) { - flatSize += subArray.size() - }; - - var outer = 0; - var inner = 0; - Prim.Array_tabulate( - flatSize, - func _ { - while (inner == self[outer].size()) { - inner := 0; - outer += 1 - }; - let element = self[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Create an array containing a single value. - /// - /// ```motoko include=import - /// let array = Array.singleton(2); - /// assert array == [2]; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func singleton(element : T) : [T] = [element]; - - /// Returns the size of an array. Equivalent to `array.size()`. - public func size(self : [T]) : Nat = self.size(); - - /// Returns whether an array is empty, i.e. contains zero elements. - public func isEmpty(self : [T]) : Bool = self.size() == 0; - - /// Converts an iterator to an array. - /// @deprecated M0235 - public func fromIter(iter : Types.Iter) : [T] { - var list : Types.Pure.List = null; - var size = 0; - label l loop { - switch (iter.next()) { - case (?element) { - list := ?(element, list); - size += 1 - }; - case null { break l } - } - }; - if (size == 0) { return [] }; - let array = Prim.Array_init( - size, - switch list { - case (?(h, _)) h; - case null { - Prim.trap("Array.fromIter(): unreachable") - } - } - ); - var i = size : Nat; - while (i > 0) { - i -= 1; - switch list { - case (?(h, t)) { - array[i] := h; - list := t - }; - case null { - Prim.trap("Array.fromIter(): unreachable") - } - } - }; - Prim.Array_tabulate(size, func i = array[i]) - }; - - /// Returns an iterator (`Iter`) over the indices of `array`. - /// An iterator provides a single method `next()`, which returns - /// indices in order, or `null` when out of index to iterate over. - /// - /// Note: You can also use `array.keys()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// let array = [10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.keys()) { - /// sum += element; - /// }; - /// assert sum == 3; // 0 + 1 + 2 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func keys(self : [T]) : Types.Iter = self.keys(); - - /// Iterator provides a single method `next()`, which returns - /// elements in order, or `null` when out of elements to iterate over. - /// - /// Note: You can also use `array.values()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// let array = [10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.values()) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func values(self : [T]) : Types.Iter = self.values(); - - /// Iterator provides a single method `next()`, which returns - /// pairs of (index, element) in order, or `null` when out of elements to iterate over. - /// - /// ```motoko include=import - /// let array = [10, 11, 12]; - /// - /// var sum = 0; - /// for ((index, element) in Array.enumerate(array)) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func enumerate(self : [T]) : Types.Iter<(Nat, T)> = object { - let size = self.size(); - var index = 0; - public func next() : ?(Nat, T) { - if (index >= size) { - return null - }; - let i = index; - index += 1; - ?(i, self[i]) - } - }; - - /// Returns true if all elements in `array` satisfy the predicate function. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4]; - /// assert Array.all(array, func x = x > 0); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func all(self : [T], predicate : T -> Bool) : Bool { - for (element in self.values()) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Returns true if any element in `array` satisfies the predicate function. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4]; - /// assert Array.any(array, func x = x > 3); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func any(self : [T], predicate : T -> Bool) : Bool { - for (element in self.values()) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Returns the index of the first `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.indexOf(array, Char.equal, 'c') == ?0; - /// assert Array.indexOf(array, Char.equal, 'f') == ?2; - /// assert Array.indexOf(array, Char.equal, 'g') == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func indexOf(self : [T], equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = nextIndexOf(self, equal, element, 0); - - /// Returns the index of the next occurence of `element` in the `array` starting from the `from` index (inclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.nextIndexOf(array, Char.equal, 'c', 0) == ?0; - /// assert Array.nextIndexOf(array, Char.equal, 'f', 0) == ?2; - /// assert Array.nextIndexOf(array, Char.equal, 'f', 2) == ?2; - /// assert Array.nextIndexOf(array, Char.equal, 'f', 3) == ?3; - /// assert Array.nextIndexOf(array, Char.equal, 'f', 4) == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func nextIndexOf(self : [T], equal : (implicit : (T, T) -> Bool), element : T, fromInclusive : Nat) : ?Nat { - var index = fromInclusive; - let size = self.size(); - while (index < size) { - if (equal(self[index], element)) { - return ?index - } else { - index += 1 - } - }; - null - }; - - /// Returns the index of the last `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.lastIndexOf(array, Char.equal, 'c') == ?0; - /// assert Array.lastIndexOf(array, Char.equal, 'f') == ?3; - /// assert Array.lastIndexOf(array, Char.equal, 'e') == ?5; - /// assert Array.lastIndexOf(array, Char.equal, 'g') == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func lastIndexOf(self : [T], equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = prevIndexOf(self, equal, element, self.size()); - - /// Returns the index of the previous occurence of `element` in the `array` starting from the `from` index (exclusive). - /// - /// Negative indices are relative to the end of the array. For example, `-1` corresponds to the last element in the array. - /// - /// If the indices are out of bounds, they are clamped to the array bounds. - /// If the first index is greater than the second, the function returns an empty iterator. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.prevIndexOf(array, Char.equal, 'c', array.size()) == ?0; - /// assert Array.prevIndexOf(array, Char.equal, 'e', array.size()) == ?5; - /// assert Array.prevIndexOf(array, Char.equal, 'e', 5) == ?4; - /// assert Array.prevIndexOf(array, Char.equal, 'e', 4) == null; - /// ``` - /// - /// Runtime: O(array.size()); - /// Space: O(1); - public func prevIndexOf(self : [T], equal : (implicit : (T, T) -> Bool), element : T, fromExclusive : Nat) : ?Nat { - var i = fromExclusive; - while (i > 0) { - i -= 1; - if (equal(self[i], element)) { - return ?i - } - }; - null - }; - - /// Returns true if the `array` contains `element` using the provided `equal` function. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.contains(array, Char.equal, 'f'); - /// assert not Array.contains(array, Char.equal, 'g'); - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func contains(self : [T], equal : (implicit : (T, T) -> Bool), element : T) : Bool { - for (item in self.vals()) { - if (equal(item, element)) { - return true - } - }; - false - }; - - /// Returns an iterator over a slice of `array` starting at `fromInclusive` up to (but not including) `toExclusive`. - /// - /// Negative indices are relative to the end of the array. For example, `-1` corresponds to the last element in the array. - /// - /// If the indices are out of bounds, they are clamped to the array bounds. - /// If the first index is greater than the second, the function returns an empty iterator. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4, 5]; - /// let iter1 = Array.range(array, 3, array.size()); - /// assert iter1.next() == ?4; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// let iter2 = Array.range(array, 3, -1); - /// assert iter2.next() == ?4; - /// assert iter2.next() == null; - /// - /// let iter3 = Array.range(array, 0, 0); - /// assert iter3.next() == null; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func range(self : [T], fromInclusive : Int, toExclusive : Int) : Types.Iter { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - object { - var pos = start; - public func next() : ?T { - if (pos >= end) { - null - } else { - let elem = self[pos]; - pos += 1; - ?elem - } - } - } - }; - - /// Returns a new array containing elements from `array` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4, 5]; - /// - /// let slice1 = Array.sliceToArray(array, 1, 4); - /// assert slice1 == [2, 3, 4]; - /// - /// let slice2 = Array.sliceToArray(array, 1, -1); - /// assert slice2 == [2, 3, 4]; - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToArray(self : [T], fromInclusive : Int, toExclusive : Int) : [T] { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - if (start >= end) { - return [] - }; - Prim.Array_tabulate(end - start, func i = self[start + i]) - }; - - /// Returns a new mutable array containing elements from `array` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// import VarArray "mo:core/VarArray"; - /// import Nat "mo:core/Nat"; - /// - /// let array = [1, 2, 3, 4, 5]; - /// - /// let slice1 = Array.sliceToVarArray(array, 1, 4); - /// assert VarArray.equal(slice1, [var 2, 3, 4], Nat.equal); - /// - /// let slice2 = Array.sliceToVarArray(array, 1, -1); - /// assert VarArray.equal(slice2, [var 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToVarArray(self : [T], fromInclusive : Int, toExclusive : Int) : [var T] { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - if (start >= end) { - return [var] - }; - Prim.Array_tabulateVar(end - start, func i = self[start + i]) - }; - - /// Converts the array to its textual representation using `f` to convert each element to `Text`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [1, 2, 3]; - /// let text = Array.toText(array, Nat.toText); - /// assert text == "[1, 2, 3]"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func toText(self : [T], f : (implicit : (toText : T -> Text))) : Text { - let size = self.size(); - if (size == 0) { return "[]" }; - var text = "["; - var i = 0; - while (i < size) { - if (i != 0) { - text #= ", " - }; - text #= f(self[i]); - i += 1 - }; - text #= "]"; - text - }; - - /// Compares two arrays using the provided comparison function for elements. - /// Returns #less, #equal, or #greater if `array1` is less than, equal to, - /// or greater than `array2` respectively. - /// - /// If arrays have different sizes but all elements up to the shorter length are equal, - /// the shorter array is considered #less than the longer array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array1 = [1, 2, 3]; - /// let array2 = [1, 2, 4]; - /// assert Array.compare(array1, array2, Nat.compare) == #less; - /// ``` - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array3 = [1, 2]; - /// let array4 = [1, 2, 3]; - /// assert Array.compare(array3, array4, Nat.compare) == #less; - /// ``` - /// - /// Runtime: O(min(size1, size2)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func compare(self : [T], other : [T], compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let size1 = self.size(); - let size2 = other.size(); - var i = 0; - let minSize = if (size1 < size2) { size1 } else { size2 }; - while (i < minSize) { - switch (compare(self[i], other[i])) { - case (#less) { return #less }; - case (#greater) { return #greater }; - case (#equal) { i += 1 } - } - }; - if (size1 < size2) { #less } else if (size1 > size2) { #greater } else { - #equal - } - }; - - /// Performs binary search on a sorted array to find the index of the `element`. - /// - /// Returns `#found(index)` if the element is found, or `#insertionIndex(index)` with the index - /// where the element would be inserted according to the ordering if not found. - /// - /// If there are multiple equal elements, no guarantee is made about which index is returned. - /// The array must be sorted in ascending order according to the `compare` function. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let sorted = [1, 3, 5, 7, 9, 11]; - /// assert Array.binarySearch(sorted, Nat.compare, 5) == #found(2); - /// assert Array.binarySearch(sorted, Nat.compare, 6) == #insertionIndex(3); - /// ``` - /// - /// Runtime: O(log(size)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func binarySearch(self : [T], compare : (implicit : (T, T) -> Order.Order), element : T) : { - #found : Nat; - #insertionIndex : Nat - } { - var left = 0; - var right = self.size(); - while (left < right) { - let mid = (left + right) / 2; - switch (compare(self[mid], element)) { - case (#less) left := mid + 1; - case (#greater) right := mid; - case (#equal) return #found mid - } - }; - #insertionIndex left - }; - - /// Checks whether the `array` is sorted according to the `compare` function. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [1, 2, 3]; - /// assert Array.isSorted(array, Nat.compare); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func isSorted(self : [T], compare : (implicit : (T, T) -> Order.Order)) : Bool { - let size = self.size(); - if (size <= 1) return true; - var i = 1; - while (i < size) { - switch (compare(self[i - 1], self[i])) { - case (#greater) return false; - case _ { i += 1 } - } - }; - true - } -} diff --git a/.mops/core@2.4.0/src/Base64.mo b/.mops/core@2.4.0/src/Base64.mo deleted file mode 100644 index 97e1bc8..0000000 --- a/.mops/core@2.4.0/src/Base64.mo +++ /dev/null @@ -1,138 +0,0 @@ -/// Module for Base64 encoding of byte sequences. -/// -/// Base64 encoding converts binary data to an ASCII string using 64 printable -/// characters, as specified in [RFC 4648](https://www.rfc-editor.org/rfc/rfc4648). -/// It is widely used for HTTP Basic Authentication, encoding binary data in -/// JSON payloads, and data URIs. -/// -/// This module uses the standard Base64 alphabet (`A–Z`, `a–z`, `0–9`, `+`, `/`) -/// and pads output to a multiple of 4 characters using `=`. -/// -/// Original version authored by Claude Sonnet (claude-sonnet-4-6) for use in generated -/// Motoko API clients. The module received subsequent manual performance improvements. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Base64 "mo:core/Base64"; -/// ``` - -import Blob "Blob"; -import Nat8 "Nat8"; -import Nat16 "Nat16"; -import Nat32 "Nat32"; -import Nat64 "Nat64"; -import Text "Text"; -import Prim "mo:prim"; - -module { - - // Standard Base64 alphabet (RFC 4648 §4) in UTF8 values. - // Equivalent to Text form: - /* - private let alphabet : [Text] = [ - "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", - "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", - "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", - "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/" - ]; - */ - // prettier-ignore - private let alphabet : [Nat8] = [ - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, - 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, - 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, - 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - 43, 47 - ]; - - /// Encodes a `Blob` as a Base64 `Text` string (RFC 4648 §4). - /// - /// Output length is always a multiple of 4, padded with `=` as needed. - /// An empty `Blob` encodes to an empty `Text`. - /// - /// Example: - /// ```motoko include=import - /// assert Base64.encode("" : Blob) == ""; - /// assert Base64.encode("f" : Blob) == "Zg=="; - /// assert Base64.encode("fo" : Blob) == "Zm8="; - /// assert Base64.encode("foo" : Blob) == "Zm9v"; - /// assert Base64.encode("foobar" : Blob) == "Zm9vYmFy"; - /// ``` - /// - /// Typical use — embedding text in a data URI: - /// ```motoko include=import - /// let payload = "Hello" : Blob; - /// let uri = "data:text/plain;base64," # Base64.encode(payload); - /// assert uri == "data:text/plain;base64,SGVsbG8="; - /// ``` - public func encode(data : Blob) : Text { - let sz = Nat64.fromIntWrap(data.size()); - var result = ""; - var i = 0 : Nat64; - var next_i = 6 : Nat64; - - // Process chunks of 6 input bytes at a time (8 output characters) - while (next_i <= sz) { - let b1 = data[i.toNat()]; - let b2 : Nat8 = data[(i +% 1).toNat()]; - let b3 : Nat8 = data[(i +% 2).toNat()]; - let b4 : Nat8 = data[(i +% 3).toNat()]; - let b5 : Nat8 = data[(i +% 4).toNat()]; - let b6 : Nat8 = data[(i +% 5).toNat()]; - - let n = (b1.toNat16().toNat32() << 16) | (b2.toNat16().toNat32() << 8) | b3.toNat16().toNat32(); - let m = (b4.toNat16().toNat32() << 16) | (b5.toNat16().toNat32() << 8) | b6.toNat16().toNat32(); - - let bytes = Blob.fromArray([ - alphabet[((n >> 18) & 0x3F).toNat()], - alphabet[((n >> 12) & 0x3F).toNat()], - alphabet[((n >> 6) & 0x3F).toNat()], - alphabet[(n & 0x3F).toNat()], - alphabet[((m >> 18) & 0x3F).toNat()], - alphabet[((m >> 12) & 0x3F).toNat()], - alphabet[((m >> 6) & 0x3F).toNat()], - alphabet[(m & 0x3F).toNat()] - ]); - - switch (Text.decodeUtf8(bytes)) { - case (?t) result := result # t; - case (_) { - Prim.trap("Cannot happen: Utf8 decode error in Base64.encode().") - } - }; - - i := next_i; - next_i +%= 6 - }; - - // Process remaining 0-5 input bytes in chunks of 3 - while (i < sz) { - let b1 = data[i.toNat()]; - let b2 : Nat8 = if (i +% 1 < sz) data[(i +% 1).toNat()] else 0; - let b3 : Nat8 = if (i +% 2 < sz) data[(i +% 2).toNat()] else 0; - - let n = (b1.toNat16().toNat32() << 16) | (b2.toNat16().toNat32() << 8) | b3.toNat16().toNat32(); - - // Note: Value 61 is the UTF8 encoding of the `=` character - let bytes = Blob.fromArray([ - alphabet[((n >> 18) & 0x3F).toNat()], - alphabet[((n >> 12) & 0x3F).toNat()], - if (i +% 1 < sz) alphabet[((n >> 6) & 0x3F).toNat()] else 61, - if (i +% 2 < sz) alphabet[(n & 0x3F).toNat()] else 61 - ]); - - switch (Text.decodeUtf8(bytes)) { - case (?t) result := result # t; - case (_) { - Prim.trap("Cannot happen: Utf8 decode error in Base64.encode().") - } - }; - - i +%= 3 - }; - result - }; - -} diff --git a/.mops/core@2.4.0/src/Blob.mo b/.mops/core@2.4.0/src/Blob.mo deleted file mode 100644 index 64de595..0000000 --- a/.mops/core@2.4.0/src/Blob.mo +++ /dev/null @@ -1,242 +0,0 @@ -/// Module for working with Blobs (immutable sequences of bytes). -/// -/// Blobs represent sequences of bytes. They are immutable, iterable, but not indexable and can be empty. -/// -/// Byte sequences are also often represented as `[Nat8]`, i.e. an array of bytes, but this representation is currently much less compact than `Blob`, taking 4 physical bytes to represent each logical byte in the sequence. -/// If you would like to manipulate Blobs, it is recommended that you convert -/// Blobs to `[var Nat8]` or `Buffer`, do the manipulation, then convert back. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Blob "mo:core/Blob"; -/// ``` -/// -/// Some built in features not listed in this module: -/// -/// * You can create a `Blob` literal from a `Text` literal, provided the context expects an expression of type `Blob`. -/// * `b.size() : Nat` returns the number of bytes in the blob `b`; -/// * `b.values() : Iter.Iter` returns an iterator to enumerate the bytes of the blob `b`. -/// -/// For example: -/// ```motoko include=import -/// import Debug "mo:core/Debug"; -/// import Nat8 "mo:core/Nat8"; -/// -/// let blob = "\00\00\00\ff" : Blob; // blob literals, where each byte is delimited by a back-slash and represented in hex -/// let blob2 = "charsもあり" : Blob; // you can also use characters in the literals -/// let numBytes = blob.size(); -/// assert numBytes == 4; // returns the number of bytes in the Blob -/// for (byte in blob.values()) { // iterator over the Blob -/// Debug.print(Nat8.toText(byte)) -/// } -/// ``` - -import Types "Types"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - public type Blob = Prim.Types.Blob; - - /// Returns an empty `Blob` (equivalent to `""`). - /// - /// Example: - /// ```motoko include=import - /// let emptyBlob = Blob.empty(); - /// assert emptyBlob.size() == 0; - /// ``` - public func empty() : Blob = ""; - - /// Returns whether the given `Blob` is empty (has a size of zero). - /// - /// ```motoko include=import - /// let blob1 = "" : Blob; - /// let blob2 = "\FF\00" : Blob; - /// assert Blob.isEmpty(blob1); - /// assert not Blob.isEmpty(blob2); - /// ``` - public func isEmpty(self : Blob) : Bool = self == ""; - - /// Returns the number of bytes in the given `Blob`. - /// This is equivalent to `blob.size()`. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\FF\00\AA" : Blob; - /// assert Blob.size(blob) == 3; - /// assert blob.size() == 3; - /// ``` - public func size(self : Blob) : Nat = self.size(); - - /// Creates a `Blob` from an array of bytes (`[Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// let bytes : [Nat8] = [0, 255, 0]; - /// let blob = Blob.fromArray(bytes); - /// assert blob == "\00\FF\00"; - /// ``` - public let fromArray : (bytes : [Nat8]) -> Blob = Prim.arrayToBlob; - - /// Creates a `Blob` from a mutable array of bytes (`[var Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// let bytes : [var Nat8] = [var 0, 255, 0]; - /// let blob = Blob.fromVarArray(bytes); - /// assert blob == "\00\FF\00"; - /// ``` - public let fromVarArray : (bytes : [var Nat8]) -> Blob = Prim.arrayMutToBlob; - - /// Converts a `Blob` to an array of bytes (`[Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\00\FF\00" : Blob; - /// let bytes = Blob.toArray(blob); - /// assert bytes == [0, 255, 0]; - /// ``` - public let toArray : (self : Blob) -> [Nat8] = Prim.blobToArray; - - /// Converts a `Blob` to a mutable array of bytes (`[var Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// import Nat8 "mo:core/Nat8"; - /// import VarArray "mo:core/VarArray"; - /// - /// let blob = "\00\FF\00" : Blob; - /// let bytes = Blob.toVarArray(blob); - /// assert VarArray.equal(bytes, [var 0, 255, 0], Nat8.equal); - /// ``` - public let toVarArray : (self : Blob) -> [var Nat8] = Prim.blobToArrayMut; - - /// Returns the (non-cryptographic) hash of `blob`. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\00\FF\00" : Blob; - /// let h = Blob.hash(blob); - /// assert h == 1_818_567_776; - /// ``` - public let hash : (self : Blob) -> Types.Hash = Prim.hashBlob; - - /// General purpose comparison function for `Blob` by comparing the value of - /// the bytes. Returns the `Order` (either `#less`, `#equal`, or `#greater`) - /// by comparing `blob1` with `blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\00\00" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// let result = Blob.compare(blob1, blob2); - /// assert result == #less; - /// ``` - public func compare(self : Blob, other : Blob) : Order.Order { - let c = Prim.blobCompare(self, other); - if (c < 0) #less else if (c == 0) #equal else #greater - }; - - /// Equality function for `Blob` types. - /// This is equivalent to `blob1 == blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\FF\00" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// assert Blob.equal(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function value - /// to pass to a higher order function. - /// - /// Example: - /// ```motoko include=import - /// import List "mo:core/List"; - /// - /// let list1 = List.singleton("\00\FF\00"); - /// let list2 = List.singleton("\00\FF\00"); - /// assert List.equal(list1, list2, Blob.equal); - /// ``` - public func equal(self : Blob, other : Blob) : Bool { self == other }; - - /// Inequality function for `Blob` types. - /// This is equivalent to `blob1 != blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\AA\AA" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// assert Blob.notEqual(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func notEqual(self : Blob, other : Blob) : Bool { self != other }; - - /// "Less than" function for `Blob` types. - /// This is equivalent to `blob1 < blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\AA\AA" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// assert Blob.less(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func less(self : Blob, other : Blob) : Bool { self < other }; - - /// "Less than or equal to" function for `Blob` types. - /// This is equivalent to `blob1 <= blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\AA\AA" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// assert Blob.lessOrEqual(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func lessOrEqual(self : Blob, other : Blob) : Bool { self <= other }; - - /// "Greater than" function for `Blob` types. - /// This is equivalent to `blob1 > blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\BB\AA\AA" : Blob; - /// let blob2 = "\00\00\00" : Blob; - /// assert Blob.greater(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func greater(self : Blob, other : Blob) : Bool { self > other }; - - /// "Greater than or equal to" function for `Blob` types. - /// This is equivalent to `blob1 >= blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\BB\AA\AA" : Blob; - /// let blob2 = "\00\00\00" : Blob; - /// assert Blob.greaterOrEqual(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func greaterOrEqual(self : Blob, other : Blob) : Bool { - self >= other - }; - -} diff --git a/.mops/core@2.4.0/src/Bool.mo b/.mops/core@2.4.0/src/Bool.mo deleted file mode 100644 index b62e053..0000000 --- a/.mops/core@2.4.0/src/Bool.mo +++ /dev/null @@ -1,126 +0,0 @@ -/// Boolean type and operations. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Bool "mo:core/Bool"; -/// ``` -/// -/// While boolean operators `_ and _` and `_ or _` are short-circuiting, -/// avoiding computation of the right argument when possible, the functions -/// `logicalAnd(_, _)` and `logicalOr(_, _)` are *strict* and will always evaluate *both* -/// of their arguments. -/// -/// Example: -/// ```motoko include=import -/// let t = true; -/// let f = false; -/// -/// // Short-circuiting AND -/// assert not (t and f); -/// -/// // Short-circuiting OR -/// assert t or f; -/// ``` - -import Prim "mo:⛔"; -import Iter "Iter"; -import Order "Order"; - -module { - - /// Booleans with constants `true` and `false`. - public type Bool = Prim.Types.Bool; - - /// Returns `a and b`. - /// - /// Example: - /// ```motoko include=import - /// assert not Bool.logicalAnd(true, false); - /// assert Bool.logicalAnd(true, true); - /// ``` - public func logicalAnd(self : Bool, other : Bool) : Bool = self and other; - - /// Returns `a or b`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.logicalOr(true, false); - /// assert Bool.logicalOr(false, true); - /// ``` - public func logicalOr(self : Bool, other : Bool) : Bool = self or other; - - /// Returns exclusive or of `a` and `b`, `a != b`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.logicalXor(true, false); - /// assert not Bool.logicalXor(true, true); - /// assert not Bool.logicalXor(false, false); - /// ``` - public func logicalXor(self : Bool, other : Bool) : Bool = self != other; - - /// Returns `not bool`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.logicalNot(false); - /// assert not Bool.logicalNot(true); - /// ``` - public func logicalNot(self : Bool) : Bool = not self; - - /// Returns `a == b`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.equal(true, true); - /// assert not Bool.equal(true, false); - /// ``` - public func equal(self : Bool, other : Bool) : Bool { self == other }; - - /// Returns the ordering of `a` compared to `b`. - /// Returns `#less` if `a` is `false` and `b` is `true`, - /// `#equal` if `a` equals `b`, - /// and `#greater` if `a` is `true` and `b` is `false`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.compare(true, false) == #greater; - /// assert Bool.compare(true, true) == #equal; - /// assert Bool.compare(false, true) == #less; - /// ``` - public func compare(self : Bool, other : Bool) : Order.Order { - if (self == other) #equal else if self #greater else #less - }; - - /// Returns a text value which is either `"true"` or `"false"` depending on the input value. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.toText(true) == "true"; - /// assert Bool.toText(false) == "false"; - /// ``` - public func toText(self : Bool) : Text { - if self "true" else "false" - }; - - /// Returns an iterator over all possible boolean values (`true` and `false`). - /// - /// Example: - /// ```motoko include=import - /// let iter = Bool.allValues(); - /// assert iter.next() == ?true; - /// assert iter.next() == ?false; - /// assert iter.next() == null; - /// ``` - public func allValues() : Iter.Iter = object { - var state : ?Bool = ?true; - public func next() : ?Bool { - switch state { - case (?true) { state := ?false; ?true }; - case (?false) { state := null; ?false }; - case null { null } - } - } - }; - -} diff --git a/.mops/core@2.4.0/src/CertifiedData.mo b/.mops/core@2.4.0/src/CertifiedData.mo deleted file mode 100644 index f3ffb82..0000000 --- a/.mops/core@2.4.0/src/CertifiedData.mo +++ /dev/null @@ -1,54 +0,0 @@ -/// Certified data. -/// -/// The Internet Computer allows canister smart contracts to store a small amount of data during -/// update method processing so that during query call processing, the canister can obtain -/// a certificate about that data. -/// -/// This module provides a _low-level_ interface to this API, aimed at advanced -/// users and library implementors. See the Internet Computer Functional -/// Specification and corresponding documentation for how to use this to make query -/// calls to your canister tamperproof. - -import Prim "mo:⛔"; - -module { - - /// Set the certified data. - /// - /// Must be called from an update method, else traps. - /// Must be passed a blob of at most 32 bytes, else traps. - /// - /// Example: - /// ```motoko no-repl - /// import CertifiedData "mo:core/CertifiedData"; - /// import Blob "mo:core/Blob"; - /// - /// // Must be in an update call - /// - /// let array : [Nat8] = [1, 2, 3]; - /// let blob = Blob.fromArray(array); - /// CertifiedData.set(blob); - /// ``` - /// - /// See a full example on how to use certified variables here: https://github.com/dfinity/examples/tree/master/motoko/cert-var - /// - public let set : (data : Blob) -> () = Prim.setCertifiedData; - - /// Gets a certificate - /// - /// Returns `null` if no certificate is available, e.g. when processing an - /// update call or inter-canister call. This returns a non-`null` value only - /// when processing a query call. - /// - /// Example: - /// ```motoko no-repl - /// import CertifiedData "mo:core/CertifiedData"; - /// // Must be in a query call - /// - /// CertifiedData.getCertificate(); - /// ``` - /// See a full example on how to use certified variables here: https://github.com/dfinity/examples/tree/master/motoko/cert-var - /// - public let getCertificate : () -> ?Blob = Prim.getCertificate; - -} diff --git a/.mops/core@2.4.0/src/Char.mo b/.mops/core@2.4.0/src/Char.mo deleted file mode 100644 index 4dc75f5..0000000 --- a/.mops/core@2.4.0/src/Char.mo +++ /dev/null @@ -1,216 +0,0 @@ -/// Module for working with Characters (Unicode code points). -/// -/// Characters in Motoko represent Unicode code points -/// in the range 0 to 0x10FFFF, excluding the surrogate code points -/// (0xD800 through 0xDFFF). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Char "mo:core/Char"; -/// ``` -/// -/// Some built in features not listed in this module: -/// -/// * You can create a `Char` literal using single quotes, e.g. 'A', '1', '漢' -/// * You can compare characters using `<`, `<=`, `==`, `!=`, `>=`, `>` operators -/// * You can convert a single-character `Text` to a `Char` using `:Char` type annotation -/// -/// For example: -/// ```motoko include=import -/// let char : Char = 'A'; -/// let unicodeChar = '漢'; -/// let digit = '7'; -/// assert Char.isDigit(digit); -/// assert Char.toText(char) == "A"; -/// ``` - -import Prim "mo:⛔"; - -module { - - /// Characters represented as Unicode code points. - public type Char = Prim.Types.Char; - - /// Convert character `char` to a word containing its Unicode scalar value. - /// - /// Example: - /// ```motoko include=import - /// let char = 'A'; - /// let unicode = Char.toNat32(char); - /// assert unicode == 65; - /// ``` - public let toNat32 : (self : Char) -> Nat32 = Prim.charToNat32; - - /// Convert `w` to a character. - /// Traps if `w` is not a valid Unicode scalar value. - /// Value `w` is valid if, and only if, `w < 0xD800 or (0xE000 <= w and w <= 0x10FFFF)`. - /// - /// Example: - /// ```motoko include=import - /// let unicode : Nat32 = 65; - /// let char = Char.fromNat32(unicode); - /// assert char == 'A'; - /// ``` - public let fromNat32 : (nat32 : Nat32) -> Char = Prim.nat32ToChar; - - /// Convert character `char` to single character text. - /// - /// Example: - /// ```motoko include=import - /// let char = '漢'; - /// let text = Char.toText(char); - /// assert text == "漢"; - /// ``` - public let toText : (self : Char) -> Text = Prim.charToText; - - // Not exposed pending multi-char implementation. - private let _toUpper : (char : Char) -> Char = Prim.charToUpper; - - // Not exposed pending multi-char implementation. - private let _toLower : (char : Char) -> Char = Prim.charToLower; - - /// Returns `true` when `char` is a decimal digit between `0` and `9`, otherwise `false`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isDigit('5'); - /// assert not Char.isDigit('A'); - /// ``` - public func isDigit(self : Char) : Bool { - Prim.charToNat32(self) -% Prim.charToNat32('0') <= (9 : Nat32) - }; - - /// Returns whether `char` is a whitespace character. - /// Whitespace characters include space, tab, newline, etc. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isWhitespace(' '); - /// assert Char.isWhitespace('\n'); - /// assert not Char.isWhitespace('A'); - /// ``` - public let isWhitespace : (self : Char) -> Bool = Prim.charIsWhitespace; - - /// Returns whether `char` is a lowercase character. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isLower('a'); - /// assert not Char.isLower('A'); - /// ``` - public let isLower : (self : Char) -> Bool = Prim.charIsLowercase; - - /// Returns whether `char` is an uppercase character. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isUpper('A'); - /// assert not Char.isUpper('a'); - /// ``` - public let isUpper : (self : Char) -> Bool = Prim.charIsUppercase; - - /// Returns whether `char` is an alphabetic character. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isAlphabetic('A'); - /// assert Char.isAlphabetic('漢'); - /// assert not Char.isAlphabetic('1'); - /// ``` - public func isAlphabetic(self : Char) : Bool = Prim.charIsAlphabetic(self); - - /// Returns `a == b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.equal('A', 'A'); - /// assert not Char.equal('A', 'B'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func equal(self : Char, other : Char) : Bool { self == other }; - - /// Returns `a != b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.notEqual('A', 'B'); - /// assert not Char.notEqual('A', 'A'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func notEqual(self : Char, other : Char) : Bool { self != other }; - - /// Returns `a < b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.less('A', 'B'); - /// assert not Char.less('B', 'A'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func less(self : Char, other : Char) : Bool { self < other }; - - /// Returns `a <= b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.lessOrEqual('A', 'A'); - /// assert Char.lessOrEqual('A', 'B'); - /// assert not Char.lessOrEqual('B', 'A'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func lessOrEqual(self : Char, other : Char) : Bool { self <= other }; - - /// Returns `a > b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.greater('B', 'A'); - /// assert not Char.greater('A', 'B'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func greater(self : Char, other : Char) : Bool { self > other }; - - /// Returns `a >= b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.greaterOrEqual('B', 'A'); - /// assert Char.greaterOrEqual('A', 'A'); - /// assert not Char.greaterOrEqual('A', 'B'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func greaterOrEqual(self : Char, other : Char) : Bool { self >= other }; - - /// Returns the order of `a` and `b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.compare('A', 'B') == #less; - /// assert Char.compare('B', 'A') == #greater; - /// assert Char.compare('A', 'A') == #equal; - /// ``` - public func compare(self : Char, other : Char) : { #less; #equal; #greater } { - if (self < other) { #less } else if (self == other) { #equal } else { - #greater - } - }; - -} diff --git a/.mops/core@2.4.0/src/Cycles.mo b/.mops/core@2.4.0/src/Cycles.mo deleted file mode 100644 index 5c62828..0000000 --- a/.mops/core@2.4.0/src/Cycles.mo +++ /dev/null @@ -1,139 +0,0 @@ -/// Managing cycles within actors in the Internet Computer Protocol (ICP). -/// -/// The usage of the Internet Computer is measured, and paid for, in _cycles_. -/// This library provides imperative operations for observing cycles, transferring cycles, and -/// observing refunds of cycles. -/// -/// **NOTE:** Since cycles measure computational resources, the value of `balance()` can change from one call to the next. -/// -/// Cycles can be transferred from the current actor to another actor with the evaluation of certain forms of expression. -/// In particular, the expression must be a call to a shared function, a call to a local function with an `async` return type, or a simple `async` expression. -/// To attach an amount of cycles to an expression ``, simply prefix the expression with `(with cycles = )`, that is, `(with cycles = ) `. -/// -/// **NOTE:** Attaching cycles will trap if the amount specified exceeds `2 ** 128` cycles. -/// -/// Upon the call, but not before, the amount of cycles is deducted from `balance()`. -/// If this total exceeds `balance()`, the caller traps, aborting the call without consuming the cycles. -/// Note that attaching cycles to a call to a local function call or `async` expression just transfers cycles from the current actor to itself. -/// -/// Example for use on the ICP: -/// ```motoko no-repl -/// import Cycles "mo:core/Cycles"; -/// -/// persistent actor { -/// public func main() : async () { -/// let initialBalance = Cycles.balance(); -/// await (with cycles = 15_000_000) operation(); // accepts 10_000_000 cycles -/// assert Cycles.refunded() == 5_000_000; -/// assert Cycles.balance() < initialBalance; // decreased by around 10_000_000 -/// }; -/// -/// func operation() : async () { -/// let initialBalance = Cycles.balance(); -/// let initialAvailable = Cycles.available(); -/// let obtained = Cycles.accept(10_000_000); -/// assert obtained == 10_000_000; -/// assert Cycles.balance() == initialBalance + 10_000_000; -/// assert Cycles.available() == initialAvailable - 10_000_000; -/// } -/// } -/// ``` -import Prim "mo:⛔"; -module { - - /// Returns the actor's current balance of cycles as `amount`. - /// - /// Example for use on the ICP: - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// public func main() : async() { - /// let balance = Cycles.balance(); - /// assert balance > 0; - /// } - /// } - /// ``` - public let balance : () -> (amount : Nat) = Prim.cyclesBalance; - - /// Returns the currently available `amount` of cycles. - /// The amount available is the amount received in the current call, - /// minus the cumulative amount `accept`ed by this call. - /// On exit from the current shared function or async expression via `return` or `throw`, - /// any remaining available amount is automatically refunded to the caller/context. - /// - /// Example for use on the ICP: - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// public func main() : async() { - /// let available = Cycles.available(); - /// assert available >= 0; - /// } - /// } - /// ``` - public let available : () -> (amount : Nat) = Prim.cyclesAvailable; - - /// Transfers up to `amount` from `available()` to `balance()`. - /// Returns the amount actually transferred, which may be less than - /// requested, for example, if less is available, or if canister balance limits are reached. - /// - /// Example for use on the ICP (for simplicity, only transferring cycles to itself): - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// public func main() : async() { - /// await (with cycles = 15_000_000) operation(); // accepts 10_000_000 cycles - /// }; - /// - /// func operation() : async() { - /// let obtained = Cycles.accept(10_000_000); - /// assert obtained == 10_000_000; - /// } - /// } - /// ``` - public let accept : (amount : Nat) -> (accepted : Nat) = Prim.cyclesAccept; - - /// Reports `amount` of cycles refunded in the last `await` of the current - /// context, or zero if no await has occurred yet. - /// Calling `refunded()` is solely informational and does not affect `balance()`. - /// Instead, refunds are automatically added to the current balance, - /// whether or not `refunded` is used to observe them. - /// - /// Example for use on the ICP (for simplicity, only transferring cycles to itself): - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// func operation() : async() { - /// ignore Cycles.accept(10_000_000); - /// }; - /// - /// public func main() : async() { - /// await (with cycles = 15_000_000) operation(); // accepts 10_000_000 cycles - /// assert Cycles.refunded() == 5_000_000; - /// } - /// } - /// ``` - public let refunded : () -> (amount : Nat) = Prim.cyclesRefunded; - - /// Attempts to burn `amount` of cycles, deducting `burned` from the canister's - /// cycle balance. The burned cycles are irrevocably lost and not available to any - /// other principal either. - /// - /// Example for use on the IC: - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// public func main() : async() { - /// let burnt = Cycles.burn(10_000_000); - /// assert burnt == 10_000_000; - /// } - /// } - /// ``` - public let burn : (amount : Nat) -> (burned : Nat) = Prim.cyclesBurn; - -} diff --git a/.mops/core@2.4.0/src/Debug.mo b/.mops/core@2.4.0/src/Debug.mo deleted file mode 100644 index 7727a8a..0000000 --- a/.mops/core@2.4.0/src/Debug.mo +++ /dev/null @@ -1,39 +0,0 @@ -/// Utility functions for debugging. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Debug "mo:core/Debug"; -/// ``` - -import Prim "mo:⛔"; -import Runtime "Runtime"; - -module { - - /// Prints `text` to output stream. - /// - /// NOTE: When running on an ICP network, all output is written to the [canister log](https://internetcomputer.org/docs/building-apps/canister-management/logs) with the exclusion of any output - /// produced during the execution of non-replicated queries and composite queries. - /// In other environments, like the interpreter and stand-alone wasm engines, the output is written to standard out. - /// - /// ```motoko include=import - /// Debug.print "Hello New World!"; - /// Debug.print(debug_show(4)) // Often used with `debug_show` to convert values to Text - /// ``` - public let print : (text : Text) -> () = Prim.debugPrint; - - /// Mark incomplete code with the `todo()` function. - /// - /// Each have calls are well-typed in all typing contexts, which - /// trap in all execution contexts. - /// - /// ```motoko include=import - /// func doSomethingComplex() { - /// Debug.todo() - /// }; - /// ``` - public func todo() : None { - Runtime.trap("Debug.todo()") - }; - -} diff --git a/.mops/core@2.4.0/src/Error.mo b/.mops/core@2.4.0/src/Error.mo deleted file mode 100644 index cf73496..0000000 --- a/.mops/core@2.4.0/src/Error.mo +++ /dev/null @@ -1,106 +0,0 @@ -/// Error values and inspection. -/// -/// The `Error` type is the argument to `throw`, parameter of `catch`. -/// The `Error` type is opaque. - -import Prim "mo:⛔"; - -module { - - /// Error value resulting from `async` computations - public type Error = Prim.Types.Error; - - /// Error code to classify different kinds of user and system errors: - /// ```motoko - /// type ErrorCode = { - /// // Fatal error. - /// #system_fatal; - /// // Transient error. - /// #system_transient; - /// // Destination invalid. - /// #destination_invalid; - /// // Canister error (e.g., trap, no response). - /// #canister_error; - /// // Explicit reject by canister code. - /// #canister_reject; - /// // Response unknown; system stopped waiting for it (e.g., timed out, or system under high load). - /// #system_unknown; - /// // Future error code (with unrecognized numeric code). - /// #future : Nat32; - /// // Error issuing inter-canister call - /// // (indicating destination queue full or freezing threshold crossed). - /// #call_error : { err_code : Nat32 } - /// }; - /// ``` - public type ErrorCode = Prim.ErrorCode; - - /// Create an error from the message with the code `#canister_reject`. - /// - /// Example: - /// ```motoko - /// import Error "mo:core/Error"; - /// - /// Error.reject("Example error") // can be used as throw argument - /// ``` - public let reject : (message : Text) -> Error = Prim.error; - - /// Returns the code of an error. - /// - /// Example: - /// ```motoko - /// import Error "mo:core/Error"; - /// - /// let error = Error.reject("Example error"); - /// Error.code(error) // #canister_reject - /// ``` - public let code : (self : Error) -> ErrorCode = Prim.errorCode; - - /// Returns the message of an error. - /// - /// Example: - /// ```motoko - /// import Error "mo:core/Error"; - /// - /// let error = Error.reject("Example error"); - /// Error.message(error) // "Example error" - /// ``` - public let message : (self : Error) -> Text = Prim.errorMessage; - - /// Checks if the error is a clean reject. - /// A clean reject means that there must be no state changes on the callee side. - public func isCleanReject(self : Error) : Bool = switch (code(self)) { - case (#system_fatal or #system_transient or #destination_invalid or #call_error _) true; - case _ false - }; - - /// Returns whether retrying to send a message may result in success. - /// - /// Example: - /// ```motoko - /// import Error "mo:core/Error"; - /// import Debug "mo:core/Debug"; - /// - /// persistent actor { - /// type CallableActor = actor { - /// call : () -> async () - /// }; - /// - /// public func example(callableActor : CallableActor) { - /// try { - /// await (with timeout = 3) callableActor.call(); - /// } - /// catch e { - /// if (Error.isRetryPossible e) { - /// Debug.print(Error.message e); - /// } - /// } - /// } - /// } - /// - /// ``` - public func isRetryPossible(self : Error) : Bool = switch (code(self)) { - case (#system_transient or #system_unknown) true; - case _ false - }; - -} diff --git a/.mops/core@2.4.0/src/Float.mo b/.mops/core@2.4.0/src/Float.mo deleted file mode 100644 index 9618fec..0000000 --- a/.mops/core@2.4.0/src/Float.mo +++ /dev/null @@ -1,829 +0,0 @@ -/// Double precision (64-bit) floating-point numbers in IEEE 754 representation. -/// -/// This module contains common floating-point constants and utility functions. -/// -/// ```motoko name=import -/// import Float "mo:core/Float"; -/// ``` -/// -/// Notation for special values in the documentation below: -/// `+inf`: Positive infinity -/// `-inf`: Negative infinity -/// `NaN`: "not a number" (can have different sign bit values, but `NaN != NaN` regardless of the sign). -/// -/// Note: -/// Floating point numbers have limited precision and operations may inherently result in numerical errors. -/// -/// Examples of numerical errors: -/// ```motoko -/// assert 0.1 + 0.1 + 0.1 != 0.3; -/// ``` -/// -/// ```motoko -/// assert not (1e16 + 1.0 != 1e16); -/// ``` -/// -/// (and many more cases) -/// -/// Advice: -/// * Floating point number comparisons by `==` or `!=` are discouraged. Instead, it is better to compare -/// floating-point numbers with a numerical tolerance, called epsilon. -/// -/// Example: -/// ```motoko -/// import Float "mo:core/Float"; -/// let x = 0.1 + 0.1 + 0.1; -/// let y = 0.3; -/// -/// let epsilon = 1e-6; // This depends on the application case (needs a numerical error analysis). -/// assert Float.equal(x, y, epsilon); -/// ``` -/// -/// * For absolute precision, it is recommened to encode the fraction number as a pair of a Nat for the base -/// and a Nat for the exponent (decimal point). -/// -/// NaN sign: -/// * The NaN sign is only applied by `abs`, `neg`, and `copySign`. Other operations can have an arbitrary -/// sign bit for NaN results. - -import Prim "mo:⛔"; -import Int "Int"; -import Order "Order"; - -module { - - /// 64-bit floating point number type. - public type Float = Prim.Types.Float; - - /// Ratio of the circumference of a circle to its diameter. - /// Note: Limited precision. - public let pi : Float = 3.14159265358979323846; // taken from musl math.h - - /// Base of the natural logarithm. - /// Note: Limited precision. - public let e : Float = 2.7182818284590452354; // taken from musl math.h - - /// Determines whether the `number` is a `NaN` ("not a number" in the floating point representation). - /// Notes: - /// * Equality test of `NaN` with itself or another number is always `false`. - /// * There exist many internal `NaN` value representations, such as positive and negative NaN, - /// signalling and quiet NaNs, each with many different bit representations. - /// - /// Example: - /// ```motoko include=import - /// assert Float.isNaN(0.0/0.0); - /// ``` - public func isNaN(self : Float) : Bool { - self != self - }; - - /// Returns the absolute value of `x`. - /// - /// Special cases: - /// ``` - /// abs(+inf) => +inf - /// abs(-inf) => +inf - /// abs(-NaN) => +NaN - /// abs(-0.0) => 0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.abs(-1.2), 1.2, epsilon); - /// ``` - public let abs : (x : Float) -> Float = Prim.floatAbs; - - /// Returns the square root of `x`. - /// - /// Special cases: - /// ``` - /// sqrt(+inf) => +inf - /// sqrt(-0.0) => -0.0 - /// sqrt(x) => NaN if x < 0.0 - /// sqrt(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.sqrt(6.25), 2.5, epsilon); - /// ``` - public let sqrt : (x : Float) -> Float = Prim.floatSqrt; - - /// Returns the smallest integral float greater than or equal to `x`. - /// - /// Special cases: - /// ``` - /// ceil(+inf) => +inf - /// ceil(-inf) => -inf - /// ceil(NaN) => NaN - /// ceil(0.0) => 0.0 - /// ceil(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.ceil(1.2), 2.0, epsilon); - /// ``` - public let ceil : (x : Float) -> Float = Prim.floatCeil; - - /// Returns the largest integral float less than or equal to `x`. - /// - /// Special cases: - /// ``` - /// floor(+inf) => +inf - /// floor(-inf) => -inf - /// floor(NaN) => NaN - /// floor(0.0) => 0.0 - /// floor(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.floor(1.2), 1.0, epsilon); - /// ``` - public let floor : (x : Float) -> Float = Prim.floatFloor; - - /// Returns the nearest integral float not greater in magnitude than `x`. - /// This is equivalent to returning `x` with truncating its decimal places. - /// - /// Special cases: - /// ``` - /// trunc(+inf) => +inf - /// trunc(-inf) => -inf - /// trunc(NaN) => NaN - /// trunc(0.0) => 0.0 - /// trunc(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.trunc(2.75), 2.0, epsilon); - /// ``` - public let trunc : (x : Float) -> Float = Prim.floatTrunc; - - /// Returns the nearest integral float to `x`. - /// A decimal place of exactly .5 is rounded to the nearest even integral float. - /// and rounded down for `x < 0` - /// - /// Special cases: - /// ``` - /// nearest(+inf) => +inf - /// nearest(-inf) => -inf - /// nearest(NaN) => NaN - /// nearest(0.0) => 0.0 - /// nearest(-0.0) => -0.0 - /// nearest(14.5) => 14.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.nearest(2.75) == 3.0 - /// ``` - public let nearest : (x : Float) -> Float = Prim.floatNearest; - - /// Returns `x` if `x` and `y` have same sign, otherwise `x` with negated sign. - /// - /// The sign bit of zero, infinity, and `NaN` is considered. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.copySign(1.2, -2.3), -1.2, epsilon); - /// ``` - public let copySign : (x : Float, y : Float) -> Float = Prim.floatCopySign; - - /// Returns the smaller value of `x` and `y`. - /// - /// Special cases: - /// ``` - /// min(NaN, y) => NaN for any Float y - /// min(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.min(1.2, -2.3) == -2.3; // with numerical imprecision - /// ``` - public let min : (x : Float, y : Float) -> Float = Prim.floatMin; - - /// Returns the larger value of `x` and `y`. - /// - /// Special cases: - /// ``` - /// max(NaN, y) => NaN for any Float y - /// max(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.max(1.2, -2.3) == 1.2; - /// ``` - public let max : (x : Float, y : Float) -> Float = Prim.floatMax; - - /// Returns the sine of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// sin(+inf) => NaN - /// sin(-inf) => NaN - /// sin(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.sin(Float.pi / 2), 1.0, epsilon); - /// ``` - public let sin : (x : Float) -> Float = Prim.sin; - - /// Returns the cosine of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// cos(+inf) => NaN - /// cos(-inf) => NaN - /// cos(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.cos(Float.pi / 2), 0.0, epsilon); - /// ``` - public let cos : (x : Float) -> Float = Prim.cos; - - /// Returns the tangent of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// tan(+inf) => NaN - /// tan(-inf) => NaN - /// tan(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.tan(Float.pi / 4), 1.0, epsilon); - /// ``` - public let tan : (x : Float) -> Float = Prim.tan; - - /// Returns the arc sine of `x` in radians. - /// - /// Special cases: - /// ``` - /// arcsin(x) => NaN if x > 1.0 - /// arcsin(x) => NaN if x < -1.0 - /// arcsin(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.arcsin(1.0), Float.pi / 2, epsilon); - /// ``` - public let arcsin : (x : Float) -> Float = Prim.arcsin; - - /// Returns the arc cosine of `x` in radians. - /// - /// Special cases: - /// ``` - /// arccos(x) => NaN if x > 1.0 - /// arccos(x) => NaN if x < -1.0 - /// arcos(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.arccos(1.0), 0.0, epsilon); - /// ``` - public let arccos : (x : Float) -> Float = Prim.arccos; - - /// Returns the arc tangent of `x` in radians. - /// - /// Special cases: - /// ``` - /// arctan(+inf) => pi / 2 - /// arctan(-inf) => -pi / 2 - /// arctan(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.arctan(1.0), Float.pi / 4, epsilon); - /// ``` - public let arctan : (x : Float) -> Float = Prim.arctan; - - /// Given `(y, x)`, returns the arc tangent in radians of `y/x` based on the signs of both values to determine the correct quadrant. - /// - /// Special cases: - /// ``` - /// arctan2(0.0, 0.0) => 0.0 - /// arctan2(-0.0, 0.0) => -0.0 - /// arctan2(0.0, -0.0) => pi - /// arctan2(-0.0, -0.0) => -pi - /// arctan2(+inf, +inf) => pi / 4 - /// arctan2(+inf, -inf) => 3 * pi / 4 - /// arctan2(-inf, +inf) => -pi / 4 - /// arctan2(-inf, -inf) => -3 * pi / 4 - /// arctan2(NaN, x) => NaN for any Float x - /// arctan2(y, NaN) => NaN for any Float y - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let sqrt2over2 = Float.sqrt(2) / 2; - /// assert Float.arctan2(sqrt2over2, sqrt2over2) == Float.pi / 4; - /// ``` - public let arctan2 : (y : Float, x : Float) -> Float = Prim.arctan2; - - /// Returns the value of `e` raised to the `x`-th power. - /// - /// Special cases: - /// ``` - /// exp(+inf) => +inf - /// exp(-inf) => 0.0 - /// exp(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.exp(1.0), Float.e, epsilon); - /// ``` - public let exp : (x : Float) -> Float = Prim.exp; - - /// Returns the natural logarithm (base-`e`) of `x`. - /// - /// Special cases: - /// ``` - /// log(0.0) => -inf - /// log(-0.0) => -inf - /// log(x) => NaN if x < 0.0 - /// log(+inf) => +inf - /// log(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.log(Float.e), 1.0, epsilon); - /// ``` - public let log : (x : Float) -> Float = Prim.log; - - /// Formatting. `format(fmt, x)` formats `x` to `Text` according to the - /// formatting directive `fmt`, which can take one of the following forms: - /// - /// * `#fix prec` as fixed-point format with `prec` digits - /// * `#exp prec` as exponential format with `prec` digits - /// * `#gen prec` as generic format with `prec` digits - /// * `#exact` as exact format that can be decoded without loss. - /// - /// `-0.0` is formatted with negative sign bit. - /// Positive infinity is formatted as "inf". - /// Negative infinity is formatted as "-inf". - /// - /// The numerical precision and the text format can vary between - /// Motoko versions and runtime configuration. Moreover, `NaN` can be printed - /// differently, i.e. "NaN" or "nan", potentially omitting the `NaN` sign. - /// - /// Example: - /// ```motoko include=import no-validate - /// assert Float.format(#exp 3, 123.0) == "1.230e+02"; - /// ``` - public func format(self : Float, fmt : { #fix : Nat8; #exp : Nat8; #gen : Nat8; #exact }) : Text = switch fmt { - case (#fix(prec)) { Prim.floatToFormattedText(self, prec, 0) }; - case (#exp(prec)) { Prim.floatToFormattedText(self, prec, 1) }; - case (#gen(prec)) { Prim.floatToFormattedText(self, prec, 2) }; - case (#exact) { Prim.floatToFormattedText(self, 17, 2) } - }; - - /// Conversion to Text. Use `format(fmt, x)` for more detailed control. - /// - /// `-0.0` is formatted with negative sign bit. - /// Positive infinity is formatted as `inf`. - /// Negative infinity is formatted as `-inf`. - /// `NaN` is formatted as `NaN` or `-NaN` depending on its sign bit. - /// - /// The numerical precision and the text format can vary between - /// Motoko versions and runtime configuration. Moreover, `NaN` can be printed - /// differently, i.e. "NaN" or "nan", potentially omitting the `NaN` sign. - /// - /// Example: - /// ```motoko include=import no-validate - /// assert Float.toText(1.2) == "1.2"; - /// ``` - public let toText : (self : Float) -> Text = Prim.floatToText; - - /// Conversion to Int64 by truncating Float, equivalent to `toInt64(trunc(f))` - /// - /// Traps if the floating point number is larger or smaller than the representable Int64. - /// Also traps for `inf`, `-inf`, and `NaN`. - /// - /// Example: - /// ```motoko include=import - /// assert Float.toInt64(-12.3) == -12; - /// ``` - public let toInt64 : (self : Float) -> Int64 = Prim.floatToInt64; - - /// Conversion from Int64. - /// - /// Note: The floating point number may be imprecise for large or small Int64. - /// - /// Example: - /// ```motoko include=import - /// assert Float.fromInt64(-42) == -42.0; - /// ``` - public let fromInt64 : (x : Int64) -> Float = Prim.int64ToFloat; - - /// Conversion to Int. - /// - /// Traps for `inf`, `-inf`, and `NaN`. - /// - /// Example: - /// ```motoko include=import - /// assert Float.toInt(1.2e6) == +1_200_000; - /// ``` - public let toInt : (self : Float) -> Int = Prim.floatToInt; - - /// Conversion from Int. May result in `Inf`. - /// - /// Note: The floating point number may be imprecise for large or small Int values. - /// Returns `inf` if the integer is greater than the maximum floating point number. - /// Returns `-inf` if the integer is less than the minimum floating point number. - /// - /// Example: - /// ```motoko include=import - /// assert Float.fromInt(-123) == -123.0; - /// ``` - /// @deprecated M0235 - public let fromInt : (x : Int) -> Float = Prim.intToFloat; - - /// Conversion to Float32 (32-bit single precision). - /// - /// Note: This may lose precision for values that are not exactly representable in 32-bit. - /// - /// Example: - /// ```motoko include=import - /// assert Float.toFloat32(1.5) == 1.5; - /// ``` - public let toFloat32 : (self : Float) -> Prim.Types.Float32 = Prim.floatToFloat32; - - /// Conversion from Float32 (32-bit single precision) to Float (64-bit double precision). - /// - /// This is a lossless widening conversion. - /// - /// Example: - /// ```motoko include=import - /// assert Float.fromFloat32(1.5) == 1.5; - /// ``` - public let fromFloat32 : (x : Prim.Types.Float32) -> Float = Prim.float32ToFloat; - - /// Determines whether `x` is equal to `y` within the defined tolerance of `epsilon`. - /// The `epsilon` considers numerical erros, see comment above. - /// Equivalent to `Float.abs(x - y) <= epsilon` for a non-negative epsilon. - /// - /// Traps if `epsilon` is negative or `NaN`. - /// - /// Special cases: - /// ``` - /// equal(+0.0, -0.0, epsilon) => true for any `epsilon >= 0.0` - /// equal(-0.0, +0.0, epsilon) => true for any `epsilon >= 0.0` - /// equal(+inf, +inf, epsilon) => true for any `epsilon >= 0.0` - /// equal(-inf, -inf, epsilon) => true for any `epsilon >= 0.0` - /// equal(x, NaN, epsilon) => false for any x and `epsilon >= 0.0` - /// equal(NaN, y, epsilon) => false for any y and `epsilon >= 0.0` - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(-12.3, -1.23e1, epsilon); - /// ``` - public func equal(x : Float, y : Float, epsilon : Float) : Bool { - if (not (epsilon >= 0.0)) { - // also considers NaN, not identical to `epsilon < 0.0` - Prim.trap("Float.equal(): epsilon must be greater or equal 0.0") - }; - x == y or abs(x - y) <= epsilon // `x == y` to also consider infinity equal - }; - - /// Determines whether `x` is not equal to `y` within the defined tolerance of `epsilon`. - /// The `epsilon` considers numerical erros, see comment above. - /// Equivalent to `not equal(x, y, epsilon)`. - /// - /// Traps if `epsilon` is negative or `NaN`. - /// - /// Special cases: - /// ``` - /// notEqual(+0.0, -0.0, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(-0.0, +0.0, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(+inf, +inf, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(-inf, -inf, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(x, NaN, epsilon) => true for any x and `epsilon >= 0.0` - /// notEqual(NaN, y, epsilon) => true for any y and `epsilon >= 0.0` - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert not Float.notEqual(-12.3, -1.23e1, epsilon); - /// ``` - public func notEqual(x : Float, y : Float, epsilon : Float) : Bool { - if (not (epsilon >= 0.0)) { - // also considers NaN, not identical to `epsilon < 0.0` - Prim.trap("Float.notEqual(): epsilon must be greater or equal 0.0") - }; - not (x == y or abs(x - y) <= epsilon) - }; - - /// Returns `x < y`. - /// - /// Special cases: - /// ``` - /// less(+0.0, -0.0) => false - /// less(-0.0, +0.0) => false - /// less(NaN, y) => false for any Float y - /// less(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.less(Float.e, Float.pi); - /// ``` - public func less(x : Float, y : Float) : Bool { x < y }; - - /// Returns `x <= y`. - /// - /// Special cases: - /// ``` - /// lessOrEqual(+0.0, -0.0) => true - /// lessOrEqual(-0.0, +0.0) => true - /// lessOrEqual(NaN, y) => false for any Float y - /// lessOrEqual(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.lessOrEqual(0.123, 0.1234); - /// ``` - public func lessOrEqual(x : Float, y : Float) : Bool { x <= y }; - - /// Returns `x > y`. - /// - /// Special cases: - /// ``` - /// greater(+0.0, -0.0) => false - /// greater(-0.0, +0.0) => false - /// greater(NaN, y) => false for any Float y - /// greater(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.greater(Float.pi, Float.e); - /// ``` - public func greater(x : Float, y : Float) : Bool { x > y }; - - /// Returns `x >= y`. - /// - /// Special cases: - /// ``` - /// greaterOrEqual(+0.0, -0.0) => true - /// greaterOrEqual(-0.0, +0.0) => true - /// greaterOrEqual(NaN, y) => false for any Float y - /// greaterOrEqual(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.greaterOrEqual(0.1234, 0.123); - /// ``` - public func greaterOrEqual(x : Float, y : Float) : Bool { - x >= y - }; - - /// Defines a total order of `x` and `y` for use in sorting. - /// - /// Note: Using this operation to determine equality or inequality is discouraged for two reasons: - /// * It does not consider numerical errors, see comment above. Use `equal(x, y, espilon)` or - /// `notEqual(x, y, epsilon)` to test for equality or inequality, respectively. - /// * `NaN` are here considered equal if their sign matches, which is different to the standard equality - /// by `==` or when using `equal()` or `notEqual()`. - /// - /// Total order: - /// * negative NaN (no distinction between signalling and quiet negative NaN) - /// * negative infinity - /// * negative numbers (including negative subnormal numbers in standard order) - /// * negative zero (`-0.0`) - /// * positive zero (`+0.0`) - /// * positive numbers (including positive subnormal numbers in standard order) - /// * positive infinity - /// * positive NaN (no distinction between signalling and quiet positive NaN) - /// - /// Example: - /// ```motoko include=import - /// assert Float.compare(0.123, 0.1234) == #less; - /// ``` - public func compare(x : Float, y : Float) : Order.Order { - if (isNaN(x)) { - if (isNegative(x)) { - if (isNaN(y) and isNegative(y)) { #equal } else { #less } - } else { - if (isNaN(y) and not isNegative(y)) { #equal } else { #greater } - } - } else if (isNaN(y)) { - if (isNegative(y)) { - #greater - } else { - #less - } - } else { - if (x == y) { #equal } else if (x < y) { #less } else { - #greater - } - } - }; - - func isNegative(self : Float) : Bool { - copySign(1.0, self) < 0.0 - }; - - /// Returns the negation of `x`, `-x` . - /// - /// Changes the sign bit for infinity. - /// - /// Special cases: - /// ``` - /// neg(+inf) => -inf - /// neg(-inf) => +inf - /// neg(+NaN) => -NaN - /// neg(-NaN) => +NaN - /// neg(+0.0) => -0.0 - /// neg(-0.0) => +0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.neg(1.23), -1.23, epsilon); - /// ``` - public func neg(x : Float) : Float { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// add(+inf, y) => +inf if y is any Float except -inf and NaN - /// add(-inf, y) => -inf if y is any Float except +inf and NaN - /// add(+inf, -inf) => NaN - /// add(NaN, y) => NaN for any Float y - /// ``` - /// The same cases apply commutatively, i.e. for `add(y, x)`. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.add(1.23, 0.123), 1.353, epsilon); - /// ``` - public func add(x : Float, y : Float) : Float { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// sub(+inf, y) => +inf if y is any Float except +inf or NaN - /// sub(-inf, y) => -inf if y is any Float except -inf and NaN - /// sub(x, +inf) => -inf if x is any Float except +inf and NaN - /// sub(x, -inf) => +inf if x is any Float except -inf and NaN - /// sub(+inf, +inf) => NaN - /// sub(-inf, -inf) => NaN - /// sub(NaN, y) => NaN for any Float y - /// sub(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.sub(1.23, 0.123), 1.107, epsilon); - /// ``` - public func sub(x : Float, y : Float) : Float { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// mul(+inf, y) => +inf if y > 0.0 - /// mul(-inf, y) => -inf if y > 0.0 - /// mul(+inf, y) => -inf if y < 0.0 - /// mul(-inf, y) => +inf if y < 0.0 - /// mul(+inf, 0.0) => NaN - /// mul(-inf, 0.0) => NaN - /// mul(NaN, y) => NaN for any Float y - /// ``` - /// The same cases apply commutatively, i.e. for `mul(y, x)`. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.mul(1.23, 1e2), 123.0, epsilon); - /// ``` - public func mul(x : Float, y : Float) : Float { x * y }; - - /// Returns the division of `x` by `y`, `x / y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// div(0.0, 0.0) => NaN - /// div(x, 0.0) => +inf for x > 0.0 - /// div(x, 0.0) => -inf for x < 0.0 - /// div(x, +inf) => 0.0 for any x except +inf, -inf, and NaN - /// div(x, -inf) => 0.0 for any x except +inf, -inf, and NaN - /// div(+inf, y) => +inf if y >= 0.0 - /// div(+inf, y) => -inf if y < 0.0 - /// div(-inf, y) => -inf if y >= 0.0 - /// div(-inf, y) => +inf if y < 0.0 - /// div(NaN, y) => NaN for any Float y - /// div(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.div(1.23, 1e2), 0.0123, epsilon); - /// ``` - public func div(x : Float, y : Float) : Float { x / y }; - - /// Returns the floating point division remainder `x % y`, - /// which is defined as `x - trunc(x / y) * y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// rem(0.0, 0.0) => NaN - /// rem(x, y) => +inf if sign(x) == sign(y) for any x and y not being +inf, -inf, or NaN - /// rem(x, y) => -inf if sign(x) != sign(y) for any x and y not being +inf, -inf, or NaN - /// rem(x, +inf) => x for any x except +inf, -inf, and NaN - /// rem(x, -inf) => x for any x except +inf, -inf, and NaN - /// rem(+inf, y) => NaN for any Float y - /// rem(-inf, y) => NaN for any Float y - /// rem(NaN, y) => NaN for any Float y - /// rem(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.rem(7.2, 2.3), 0.3, epsilon); - /// ``` - public func rem(x : Float, y : Float) : Float { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// pow(+inf, y) => +inf for any y > 0.0 including +inf - /// pow(+inf, 0.0) => 1.0 - /// pow(+inf, y) => 0.0 for any y < 0.0 including -inf - /// pow(x, +inf) => +inf if x > 0.0 or x < 0.0 - /// pow(0.0, +inf) => 0.0 - /// pow(x, -inf) => 0.0 if x > 0.0 or x < 0.0 - /// pow(0.0, -inf) => +inf - /// pow(x, y) => NaN if x < 0.0 and y is a non-integral Float - /// pow(-inf, y) => +inf if y > 0.0 and y is a non-integral or an even integral Float - /// pow(-inf, y) => -inf if y > 0.0 and y is an odd integral Float - /// pow(-inf, 0.0) => 1.0 - /// pow(-inf, y) => 0.0 if y < 0.0 - /// pow(-inf, +inf) => +inf - /// pow(-inf, -inf) => 1.0 - /// pow(NaN, y) => NaN if y != 0.0 - /// pow(NaN, 0.0) => 1.0 - /// pow(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.pow(2.5, 2.0), 6.25, epsilon); - /// ``` - public func pow(x : Float, y : Float) : Float { x ** y }; - -} diff --git a/.mops/core@2.4.0/src/Float32.mo b/.mops/core@2.4.0/src/Float32.mo deleted file mode 100644 index d0ac89f..0000000 --- a/.mops/core@2.4.0/src/Float32.mo +++ /dev/null @@ -1,850 +0,0 @@ -/// Single precision (32-bit) floating-point numbers in IEEE 754 representation. -/// -/// This module contains common floating-point constants and utility functions. -/// -/// ```motoko name=import -/// import Float32 "mo:core/Float32"; -/// ``` -/// -/// Notation for special values in the documentation below: -/// `+inf`: Positive infinity -/// `-inf`: Negative infinity -/// `NaN`: "not a number" (can have different sign bit values, but `NaN != NaN` regardless of the sign). -/// -/// Note: -/// Floating point numbers have limited precision and operations may inherently result in numerical errors. -/// `Float32` has less precision than `Float` (64-bit); only about 7 significant decimal digits. -/// -/// Examples of numerical errors: -/// ```motoko -/// assert 0.1 + 0.1 + 0.1 != 0.3; -/// ``` -/// -/// Advice: -/// * Floating point number comparisons by `==` or `!=` are discouraged. Instead, it is better to compare -/// floating-point numbers with a numerical tolerance, called epsilon. -/// -/// Example: -/// ```motoko -/// import Float32 "mo:core/Float32"; -/// let x = 0.1 + 0.1 + 0.1 : Float32; -/// let y = 0.3 : Float32; -/// -/// let epsilon = 1e-5 : Float32; // This depends on the application case (needs a numerical error analysis). -/// assert Float32.equal(x, y, epsilon); -/// ``` -/// -/// * For absolute precision, it is recommended to encode the fraction number as a pair of a Nat for the base -/// and a Nat for the exponent (decimal point). -/// -/// Note: As of `moc` 1.4, `Float32` support is experimental. -/// -/// NaN sign: -/// * The NaN sign is only applied by `abs`, `neg`, and `copySign`. Other operations can have an arbitrary -/// sign bit for NaN results. - -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 32-bit floating point number type. - public type Float32 = Prim.Types.Float32; - - /// Conversion to Float (64-bit double precision). - /// - /// This is a lossless widening conversion. - /// - /// Example: - /// ```motoko include=import - /// assert Float32.toFloat(1.5) == 1.5; - /// ``` - public let toFloat : (self : Float32) -> Float = Prim.float32ToFloat; - - /// Conversion from Float (64-bit double precision) to Float32. - /// - /// Note: This may lose precision for values that are not exactly representable in 32-bit. - /// - /// Example: - /// ```motoko include=import - /// assert Float32.fromFloat(1.5) == 1.5; - /// ``` - public let fromFloat : (x : Float) -> Float32 = Prim.floatToFloat32; - - /// Ratio of the circumference of a circle to its diameter. - /// Note: Limited precision (approximately 7 significant decimal digits). - public let pi : Float32 = 3.14159265358979323846; - - /// Base of the natural logarithm. - /// Note: Limited precision (approximately 7 significant decimal digits). - public let e : Float32 = 2.7182818284590452354; - - /// Determines whether the `number` is a `NaN` ("not a number" in the floating point representation). - /// Notes: - /// * Equality test of `NaN` with itself or another number is always `false`. - /// * There exist many internal `NaN` value representations, such as positive and negative NaN, - /// signalling and quiet NaNs, each with many different bit representations. - /// - /// Example: - /// ```motoko include=import - /// assert Float32.isNaN(0.0/0.0); - /// ``` - public func isNaN(self : Float32) : Bool { - self != self - }; - - /// Returns the absolute value of `x`. - /// - /// Special cases: - /// ``` - /// abs(+inf) => +inf - /// abs(-inf) => +inf - /// abs(-NaN) => +NaN - /// abs(-0.0) => 0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.abs(-1.2), 1.2, epsilon); - /// ``` - public func abs(x : Float32) : Float32 { - fromFloat(Prim.floatAbs(toFloat(x))) - }; - - /// Returns the square root of `x`. - /// - /// Special cases: - /// ``` - /// sqrt(+inf) => +inf - /// sqrt(-0.0) => -0.0 - /// sqrt(x) => NaN if x < 0.0 - /// sqrt(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.sqrt(6.25), 2.5, epsilon); - /// ``` - public func sqrt(x : Float32) : Float32 { - fromFloat(Prim.floatSqrt(toFloat(x))) - }; - - /// Returns the smallest integral float greater than or equal to `x`. - /// - /// Special cases: - /// ``` - /// ceil(+inf) => +inf - /// ceil(-inf) => -inf - /// ceil(NaN) => NaN - /// ceil(0.0) => 0.0 - /// ceil(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.ceil(1.2), 2.0, epsilon); - /// ``` - public func ceil(x : Float32) : Float32 { - fromFloat(Prim.floatCeil(toFloat(x))) - }; - - /// Returns the largest integral float less than or equal to `x`. - /// - /// Special cases: - /// ``` - /// floor(+inf) => +inf - /// floor(-inf) => -inf - /// floor(NaN) => NaN - /// floor(0.0) => 0.0 - /// floor(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.floor(1.2), 1.0, epsilon); - /// ``` - public func floor(x : Float32) : Float32 { - fromFloat(Prim.floatFloor(toFloat(x))) - }; - - /// Returns the nearest integral float not greater in magnitude than `x`. - /// This is equivalent to returning `x` with truncating its decimal places. - /// - /// Special cases: - /// ``` - /// trunc(+inf) => +inf - /// trunc(-inf) => -inf - /// trunc(NaN) => NaN - /// trunc(0.0) => 0.0 - /// trunc(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.trunc(2.75), 2.0, epsilon); - /// ``` - public func trunc(x : Float32) : Float32 { - fromFloat(Prim.floatTrunc(toFloat(x))) - }; - - /// Returns the nearest integral float to `x`. - /// A decimal place of exactly .5 is rounded to the nearest even integral float. - /// - /// Special cases: - /// ``` - /// nearest(+inf) => +inf - /// nearest(-inf) => -inf - /// nearest(NaN) => NaN - /// nearest(0.0) => 0.0 - /// nearest(-0.0) => -0.0 - /// nearest(14.5) => 14.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.nearest(2.75) == 3.0 - /// ``` - public func nearest(x : Float32) : Float32 { - fromFloat(Prim.floatNearest(toFloat(x))) - }; - - /// Returns `x` if `x` and `y` have same sign, otherwise `x` with negated sign. - /// - /// The sign bit of zero, infinity, and `NaN` is considered. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.copySign(1.2, -2.3), -1.2, epsilon); - /// ``` - public func copySign(x : Float32, y : Float32) : Float32 { - fromFloat(Prim.floatCopySign(toFloat(x), toFloat(y))) - }; - - /// Returns the smaller value of `x` and `y`. - /// - /// Special cases: - /// ``` - /// min(NaN, y) => NaN for any Float32 y - /// min(x, NaN) => NaN for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.min(1.2, -2.3) == -2.3; // with numerical imprecision - /// ``` - public func min(x : Float32, y : Float32) : Float32 { - fromFloat(Prim.floatMin(toFloat(x), toFloat(y))) - }; - - /// Returns the larger value of `x` and `y`. - /// - /// Special cases: - /// ``` - /// max(NaN, y) => NaN for any Float32 y - /// max(x, NaN) => NaN for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.max(1.2, -2.3) == 1.2; - /// ``` - public func max(x : Float32, y : Float32) : Float32 { - fromFloat(Prim.floatMax(toFloat(x), toFloat(y))) - }; - - /// Returns the sine of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// sin(+inf) => NaN - /// sin(-inf) => NaN - /// sin(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.sin(Float32.pi / 2.0), 1.0, epsilon); - /// ``` - public func sin(x : Float32) : Float32 { - fromFloat(Prim.sin(toFloat(x))) - }; - - /// Returns the cosine of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// cos(+inf) => NaN - /// cos(-inf) => NaN - /// cos(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.cos(Float32.pi / 2.0), 0.0, epsilon); - /// ``` - public func cos(x : Float32) : Float32 { - fromFloat(Prim.cos(toFloat(x))) - }; - - /// Returns the tangent of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// tan(+inf) => NaN - /// tan(-inf) => NaN - /// tan(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.tan(Float32.pi / 4.0), 1.0, epsilon); - /// ``` - public func tan(x : Float32) : Float32 { - fromFloat(Prim.tan(toFloat(x))) - }; - - /// Returns the arc sine of `x` in radians. - /// - /// Special cases: - /// ``` - /// arcsin(x) => NaN if x > 1.0 - /// arcsin(x) => NaN if x < -1.0 - /// arcsin(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.arcsin(1.0), Float32.pi / 2.0, epsilon); - /// ``` - public func arcsin(x : Float32) : Float32 { - fromFloat(Prim.arcsin(toFloat(x))) - }; - - /// Returns the arc cosine of `x` in radians. - /// - /// Special cases: - /// ``` - /// arccos(x) => NaN if x > 1.0 - /// arccos(x) => NaN if x < -1.0 - /// arccos(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.arccos(1.0), 0.0, epsilon); - /// ``` - public func arccos(x : Float32) : Float32 { - fromFloat(Prim.arccos(toFloat(x))) - }; - - /// Returns the arc tangent of `x` in radians. - /// - /// Special cases: - /// ``` - /// arctan(+inf) => pi / 2 - /// arctan(-inf) => -pi / 2 - /// arctan(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.arctan(1.0), Float32.pi / 4.0, epsilon); - /// ``` - public func arctan(x : Float32) : Float32 { - fromFloat(Prim.arctan(toFloat(x))) - }; - - /// Given `(y, x)`, returns the arc tangent in radians of `y/x` based on the signs of both values to determine the correct quadrant. - /// - /// Special cases: - /// ``` - /// arctan2(0.0, 0.0) => 0.0 - /// arctan2(-0.0, 0.0) => -0.0 - /// arctan2(0.0, -0.0) => pi - /// arctan2(-0.0, -0.0) => -pi - /// arctan2(+inf, +inf) => pi / 4 - /// arctan2(+inf, -inf) => 3 * pi / 4 - /// arctan2(-inf, +inf) => -pi / 4 - /// arctan2(-inf, -inf) => -3 * pi / 4 - /// arctan2(NaN, x) => NaN for any Float32 x - /// arctan2(y, NaN) => NaN for any Float32 y - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let sqrt2over2 = Float32.sqrt(2.0) / 2.0; - /// assert Float32.arctan2(sqrt2over2, sqrt2over2) == Float32.pi / 4.0; - /// ``` - public func arctan2(y : Float32, x : Float32) : Float32 { - fromFloat(Prim.arctan2(toFloat(y), toFloat(x))) - }; - - /// Returns the value of `e` raised to the `x`-th power. - /// - /// Special cases: - /// ``` - /// exp(+inf) => +inf - /// exp(-inf) => 0.0 - /// exp(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.exp(1.0), Float32.e, epsilon); - /// ``` - public func exp(x : Float32) : Float32 { - fromFloat(Prim.exp(toFloat(x))) - }; - - /// Returns the natural logarithm (base-`e`) of `x`. - /// - /// Special cases: - /// ``` - /// log(0.0) => -inf - /// log(-0.0) => -inf - /// log(x) => NaN if x < 0.0 - /// log(+inf) => +inf - /// log(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.log(Float32.e), 1.0, epsilon); - /// ``` - public func log(x : Float32) : Float32 { - fromFloat(Prim.log(toFloat(x))) - }; - - /// Formatting. `format(fmt, x)` formats `x` to `Text` according to the - /// formatting directive `fmt`, which can take one of the following forms: - /// - /// * `#fix prec` as fixed-point format with `prec` digits - /// * `#exp prec` as exponential format with `prec` digits - /// * `#gen prec` as generic format with `prec` digits - /// * `#exact` as exact format that can be decoded without loss. - /// - /// `-0.0` is formatted with negative sign bit. - /// Positive infinity is formatted as "inf". - /// Negative infinity is formatted as "-inf". - /// - /// The numerical precision and the text format can vary between - /// Motoko versions and runtime configuration. Moreover, `NaN` can be printed - /// differently, i.e. "NaN" or "nan", potentially omitting the `NaN` sign. - /// - /// Example: - /// ```motoko include=import no-validate - /// assert Float32.format(123.0 : Float32, #exp (3 : Nat8)) == "1.230e+02"; - /// ``` - public func format(self : Float32, fmt : { #fix : Nat8; #exp : Nat8; #gen : Nat8; #exact }) : Text { - let f = toFloat(self); - switch fmt { - case (#fix(prec)) { Prim.floatToFormattedText(f, prec, 0) }; - case (#exp(prec)) { Prim.floatToFormattedText(f, prec, 1) }; - case (#gen(prec)) { Prim.floatToFormattedText(f, prec, 2) }; - case (#exact) { Prim.floatToFormattedText(f, 17, 2) } - } - }; - - /// Conversion to Text. Use `format(fmt, x)` for more detailed control. - /// - /// `-0.0` is formatted with negative sign bit. - /// Positive infinity is formatted as `inf`. - /// Negative infinity is formatted as `-inf`. - /// `NaN` is formatted as `NaN` or `-NaN` depending on its sign bit. - /// - /// The numerical precision and the text format can vary between - /// Motoko versions and runtime configuration. Moreover, `NaN` can be printed - /// differently, i.e. "NaN" or "nan", potentially omitting the `NaN` sign. - /// - /// Example: - /// ```motoko include=import no-validate - /// assert Float32.toText(1.5) == "1.5"; - /// ``` - public func toText(self : Float32) : Text { - Prim.floatToText(toFloat(self)) - }; - - /// Conversion to Int64 by truncating Float32, equivalent to `toInt64(trunc(f))` - /// - /// Traps if the floating point number is larger or smaller than the representable Int64. - /// Also traps for `inf`, `-inf`, and `NaN`. - /// - /// Example: - /// ```motoko include=import - /// assert Float32.toInt64(-12.0) == -12; - /// ``` - public func toInt64(self : Float32) : Int64 { - Prim.floatToInt64(toFloat(self)) - }; - - /// Conversion from Int64. - /// - /// Note: The floating point number may be imprecise for large or small Int64. - /// - /// Example: - /// ```motoko include=import - /// assert Float32.fromInt64(-42) == -42.0; - /// ``` - public func fromInt64(x : Int64) : Float32 { - fromFloat(Prim.int64ToFloat(x)) - }; - - /// Conversion to Int. - /// - /// Traps for `inf`, `-inf`, and `NaN`. - /// - /// Example: - /// ```motoko include=import - /// assert Float32.toInt(1.0e6) == +1_000_000; - /// ``` - public func toInt(self : Float32) : Int { - Prim.floatToInt(toFloat(self)) - }; - - /// Determines whether `x` is equal to `y` within the defined tolerance of `epsilon`. - /// The `epsilon` considers numerical errors, see comment above. - /// Equivalent to `Float32.abs(x - y) <= epsilon` for a non-negative epsilon. - /// - /// Traps if `epsilon` is negative or `NaN`. - /// - /// Special cases: - /// ``` - /// equal(+0.0, -0.0, epsilon) => true for any `epsilon >= 0.0` - /// equal(-0.0, +0.0, epsilon) => true for any `epsilon >= 0.0` - /// equal(+inf, +inf, epsilon) => true for any `epsilon >= 0.0` - /// equal(-inf, -inf, epsilon) => true for any `epsilon >= 0.0` - /// equal(x, NaN, epsilon) => false for any x and `epsilon >= 0.0` - /// equal(NaN, y, epsilon) => false for any y and `epsilon >= 0.0` - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(-12.3, -1.23e1, epsilon); - /// ``` - public func equal(x : Float32, y : Float32, epsilon : Float32) : Bool { - if (not (epsilon >= (0.0 : Float32))) { - // also considers NaN, not identical to `epsilon < 0.0` - Prim.trap("Float32.equal(): epsilon must be greater or equal 0.0") - }; - x == y or abs(x - y) <= epsilon // `x == y` to also consider infinity equal - }; - - /// Determines whether `x` is not equal to `y` within the defined tolerance of `epsilon`. - /// The `epsilon` considers numerical errors, see comment above. - /// Equivalent to `not equal(x, y, epsilon)`. - /// - /// Traps if `epsilon` is negative or `NaN`. - /// - /// Special cases: - /// ``` - /// notEqual(+0.0, -0.0, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(-0.0, +0.0, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(+inf, +inf, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(-inf, -inf, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(x, NaN, epsilon) => true for any x and `epsilon >= 0.0` - /// notEqual(NaN, y, epsilon) => true for any y and `epsilon >= 0.0` - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert not Float32.notEqual(-12.3, -1.23e1, epsilon); - /// ``` - public func notEqual(x : Float32, y : Float32, epsilon : Float32) : Bool { - if (not (epsilon >= (0.0 : Float32))) { - // also considers NaN, not identical to `epsilon < 0.0` - Prim.trap("Float32.notEqual(): epsilon must be greater or equal 0.0") - }; - not (x == y or abs(x - y) <= epsilon) - }; - - /// Returns `x < y`. - /// - /// Special cases: - /// ``` - /// less(+0.0, -0.0) => false - /// less(-0.0, +0.0) => false - /// less(NaN, y) => false for any Float32 y - /// less(x, NaN) => false for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.less(Float32.e, Float32.pi); - /// ``` - public func less(x : Float32, y : Float32) : Bool { x < y }; - - /// Returns `x <= y`. - /// - /// Special cases: - /// ``` - /// lessOrEqual(+0.0, -0.0) => true - /// lessOrEqual(-0.0, +0.0) => true - /// lessOrEqual(NaN, y) => false for any Float32 y - /// lessOrEqual(x, NaN) => false for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.lessOrEqual(0.123, 0.1234); - /// ``` - public func lessOrEqual(x : Float32, y : Float32) : Bool { x <= y }; - - /// Returns `x > y`. - /// - /// Special cases: - /// ``` - /// greater(+0.0, -0.0) => false - /// greater(-0.0, +0.0) => false - /// greater(NaN, y) => false for any Float32 y - /// greater(x, NaN) => false for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.greater(Float32.pi, Float32.e); - /// ``` - public func greater(x : Float32, y : Float32) : Bool { x > y }; - - /// Returns `x >= y`. - /// - /// Special cases: - /// ``` - /// greaterOrEqual(+0.0, -0.0) => true - /// greaterOrEqual(-0.0, +0.0) => true - /// greaterOrEqual(NaN, y) => false for any Float32 y - /// greaterOrEqual(x, NaN) => false for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.greaterOrEqual(0.1234, 0.123); - /// ``` - public func greaterOrEqual(x : Float32, y : Float32) : Bool { - x >= y - }; - - /// Defines a total order of `x` and `y` for use in sorting. - /// - /// Note: Using this operation to determine equality or inequality is discouraged for two reasons: - /// * It does not consider numerical errors, see comment above. Use `equal(x, y, epsilon)` or - /// `notEqual(x, y, epsilon)` to test for equality or inequality, respectively. - /// * `NaN` are here considered equal if their sign matches, which is different to the standard equality - /// by `==` or when using `equal()` or `notEqual()`. - /// - /// Total order: - /// * negative NaN (no distinction between signalling and quiet negative NaN) - /// * negative infinity - /// * negative numbers (including negative subnormal numbers in standard order) - /// * negative zero (`-0.0`) - /// * positive zero (`+0.0`) - /// * positive numbers (including positive subnormal numbers in standard order) - /// * positive infinity - /// * positive NaN (no distinction between signalling and quiet positive NaN) - /// - /// Example: - /// ```motoko include=import - /// assert Float32.compare(0.123, 0.1234) == #less; - /// ``` - public func compare(x : Float32, y : Float32) : Order.Order { - if (isNaN(x)) { - if (isNegative(x)) { - if (isNaN(y) and isNegative(y)) { #equal } else { #less } - } else { - if (isNaN(y) and not isNegative(y)) { #equal } else { #greater } - } - } else if (isNaN(y)) { - if (isNegative(y)) { - #greater - } else { - #less - } - } else { - if (x == y) { #equal } else if (x < y) { #less } else { - #greater - } - } - }; - - func isNegative(self : Float32) : Bool { - copySign(1.0, self) < (0.0 : Float32) - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Changes the sign bit for infinity. - /// - /// Special cases: - /// ``` - /// neg(+inf) => -inf - /// neg(-inf) => +inf - /// neg(+NaN) => -NaN - /// neg(-NaN) => +NaN - /// neg(+0.0) => -0.0 - /// neg(-0.0) => +0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.neg(1.23), -1.23, epsilon); - /// ``` - public func neg(x : Float32) : Float32 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// add(+inf, y) => +inf if y is any Float32 except -inf and NaN - /// add(-inf, y) => -inf if y is any Float32 except +inf and NaN - /// add(+inf, -inf) => NaN - /// add(NaN, y) => NaN for any Float32 y - /// ``` - /// The same cases apply commutatively, i.e. for `add(y, x)`. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.add(1.23, 0.123), 1.353, epsilon); - /// ``` - public func add(x : Float32, y : Float32) : Float32 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// sub(+inf, y) => +inf if y is any Float32 except +inf or NaN - /// sub(-inf, y) => -inf if y is any Float32 except -inf and NaN - /// sub(x, +inf) => -inf if x is any Float32 except +inf and NaN - /// sub(x, -inf) => +inf if x is any Float32 except -inf and NaN - /// sub(+inf, +inf) => NaN - /// sub(-inf, -inf) => NaN - /// sub(NaN, y) => NaN for any Float32 y - /// sub(x, NaN) => NaN for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.sub(1.23, 0.123), 1.107, epsilon); - /// ``` - public func sub(x : Float32, y : Float32) : Float32 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// mul(+inf, y) => +inf if y > 0.0 - /// mul(-inf, y) => -inf if y > 0.0 - /// mul(+inf, y) => -inf if y < 0.0 - /// mul(-inf, y) => +inf if y < 0.0 - /// mul(+inf, 0.0) => NaN - /// mul(-inf, 0.0) => NaN - /// mul(NaN, y) => NaN for any Float32 y - /// ``` - /// The same cases apply commutatively, i.e. for `mul(y, x)`. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.mul(1.23, 1e2), 123.0, epsilon); - /// ``` - public func mul(x : Float32, y : Float32) : Float32 { x * y }; - - /// Returns the division of `x` by `y`, `x / y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// div(0.0, 0.0) => NaN - /// div(x, 0.0) => +inf for x > 0.0 - /// div(x, 0.0) => -inf for x < 0.0 - /// div(x, +inf) => 0.0 for any x except +inf, -inf, and NaN - /// div(x, -inf) => 0.0 for any x except +inf, -inf, and NaN - /// div(+inf, y) => +inf if y >= 0.0 - /// div(+inf, y) => -inf if y < 0.0 - /// div(-inf, y) => -inf if y >= 0.0 - /// div(-inf, y) => +inf if y < 0.0 - /// div(NaN, y) => NaN for any Float32 y - /// div(x, NaN) => NaN for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.div(1.23, 1e2), 0.0123, epsilon); - /// ``` - public func div(x : Float32, y : Float32) : Float32 { x / y }; - - /// Returns the floating point division remainder `x % y`, - /// which is defined as `x - trunc(x / y) * y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// rem(0.0, 0.0) => NaN - /// rem(x, +inf) => x for any x except +inf, -inf, and NaN - /// rem(x, -inf) => x for any x except +inf, -inf, and NaN - /// rem(+inf, y) => NaN for any Float32 y - /// rem(-inf, y) => NaN for any Float32 y - /// rem(NaN, y) => NaN for any Float32 y - /// rem(x, NaN) => NaN for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.rem(7.2, 2.3), 0.3, epsilon); - /// ``` - public func rem(x : Float32, y : Float32) : Float32 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// pow(+inf, y) => +inf for any y > 0.0 including +inf - /// pow(+inf, 0.0) => 1.0 - /// pow(+inf, y) => 0.0 for any y < 0.0 including -inf - /// pow(x, +inf) => +inf if x > 0.0 or x < 0.0 - /// pow(0.0, +inf) => 0.0 - /// pow(x, -inf) => 0.0 if x > 0.0 or x < 0.0 - /// pow(0.0, -inf) => +inf - /// pow(x, y) => NaN if x < 0.0 and y is a non-integral Float32 - /// pow(NaN, y) => NaN if y != 0.0 - /// pow(NaN, 0.0) => 1.0 - /// pow(x, NaN) => NaN for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.pow(2.5, 2.0), 6.25, epsilon); - /// ``` - public func pow(x : Float32, y : Float32) : Float32 { x ** y }; - -} diff --git a/.mops/core@2.4.0/src/Func.mo b/.mops/core@2.4.0/src/Func.mo deleted file mode 100644 index e2bb10c..0000000 --- a/.mops/core@2.4.0/src/Func.mo +++ /dev/null @@ -1,48 +0,0 @@ -/// Functions on functions, creating functions from simpler inputs. -/// -/// (Most commonly used when programming in functional style using higher-order -/// functions.) -/// -/// Import from the core package to use this module. -/// -/// ```motoko name=import -/// import Func = "mo:core/Func"; -/// ``` - -module { - - /// The composition of two functions `f` and `g` is a function that applies `g` and then `f`. - /// - /// Example: - /// ```motoko include=import - /// import Text "mo:core/Text"; - /// import Char "mo:core/Char"; - /// - /// let textFromNat32 = Func.compose(Text.fromChar, Char.fromNat32); - /// assert textFromNat32(65) == "A"; - /// ``` - public func compose(f : B -> C, g : A -> B) : A -> C { - func(x : A) : C { - f(g(x)) - } - }; - - /// The `identity` function returns its argument. - /// Example: - /// ```motoko include=import - /// assert Func.identity(10) == 10; - /// assert Func.identity(true) == true; - /// ``` - public func identity(x : A) : A = x; - - /// The const function is a _curried_ function that accepts an argument `x`, - /// and then returns a function that discards its argument and always returns - /// the `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Func.const(10)("hello") == 10; - /// assert Func.const(true)(20) == true; - /// ``` - public func const(x : A) : B -> A = func _ = x -} diff --git a/.mops/core@2.4.0/src/Int.mo b/.mops/core@2.4.0/src/Int.mo deleted file mode 100644 index 37ea9b7..0000000 --- a/.mops/core@2.4.0/src/Int.mo +++ /dev/null @@ -1,677 +0,0 @@ -/// Signed integer numbers with infinite precision (also called big integers). -/// -/// Most operations on integer numbers (e.g. addition) are available as built-in operators (e.g. `-1 + 1`). -/// This module provides equivalent functions and `Text` conversion. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int "mo:core/Int"; -/// ``` - -import Prim "mo:⛔"; -import Char "Char"; -import Runtime "Runtime"; -import Iter "Iter"; -import Order "Order"; - -module { - - /// Infinite precision signed integers. - public type Int = Prim.Types.Int; - - /// Returns the absolute value of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.abs(-12) == 12; - /// ``` - public let abs : (x : Int) -> Nat = Prim.abs; - - /// Converts an integer number to its textual representation. Textual - /// representation _do not_ contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toText(-1234) == "-1234"; - /// ``` - public func toText(self : Int) : Text { - if (self == 0) { - return "0" - }; - - let isNegative = self < 0; - var int = if isNegative { -self } else { self }; - - var text = ""; - let base = 10; - - while (int > 0) { - let rem = int % base; - text := ( - switch (rem) { - case 0 { "0" }; - case 1 { "1" }; - case 2 { "2" }; - case 3 { "3" }; - case 4 { "4" }; - case 5 { "5" }; - case 6 { "6" }; - case 7 { "7" }; - case 8 { "8" }; - case 9 { "9" }; - case _ { Runtime.unreachable() } - } - ) # text; - int := int / base - }; - - return if isNegative { "-" # text } else { text } - }; - - /// Creates a integer from its textual representation. Returns `null` - /// if the input is not a valid integer. - /// - /// The textual representation _must not_ contain underscores but may - /// begin with a '+' or '-' character. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromText("-1234") == ?-1234; - /// ``` - public func fromText(text : Text) : ?Int { - if (text == "") { - return null - }; - var n = 0; - var isFirst = true; - var isNegative = false; - var hasDigits = false; - for (c in text.chars()) { - if (isFirst and c == '+') { - // Skip character - } else if (isFirst and c == '-') { - isNegative := true - } else if (Char.isDigit(c)) { - hasDigits := true; - let charAsNat = Prim.nat32ToNat(Prim.charToNat32(c) -% Prim.charToNat32('0')); - n := n * 10 + charAsNat - } else { - return null - }; - isFirst := false - }; - if (not hasDigits) { - return null - }; - ?(if (isNegative) { -n } else { n }) - }; - - /// Creates a integer from its textual representation. Returns `null` - /// if the input is not a valid integer. - /// - /// This functions is meant to be used with contextual-dot notation. - /// - /// Example: - /// ```motoko include=import - /// assert "-1234".toInt() == ?-1234; - /// ``` - public func toInt(self : Text) : ?Int { - fromText(self) - }; - - /// Converts an integer to a natural number. Traps if the integer is negative. - /// - /// Example: - /// ```motoko include=import - /// import Debug "mo:core/Debug"; - /// assert Int.toNat(1234 : Int) == (1234 : Nat); - /// ``` - public func toNat(self : Int) : Nat { - if (self < 0) { - Runtime.trap("Int.toNat(): negative input value") - } else { - abs(self) - } - }; - - /// Converts a natural number to an integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromNat(1234 : Nat) == (1234 : Int); - /// ``` - public func fromNat(nat : Nat) : Int { - nat : Int - }; - - /// Conversion to Float. May result in `Inf`. - /// - /// Note: The floating point number may be imprecise for large or small Int values. - /// Returns `inf` if the integer is greater than the maximum floating point number. - /// Returns `-inf` if the integer is less than the minimum floating point number. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toFloat(-123) == -123.0; - /// ``` - public let toFloat : (self : Int) -> Float = Prim.intToFloat; - - /// Converts a signed integer with infinite precision to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toInt8(123) == (123 : Int8); - /// ``` - public let toInt8 : (self : Int) -> Int8 = Prim.intToInt8; - - /// Converts a signed integer with infinite precision to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toInt16(12_345) == (12_345 : Int16); - /// ``` - public let toInt16 : (self : Int) -> Int16 = Prim.intToInt16; - - /// Converts a signed integer with infinite precision to a 32-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toInt32(123_456) == (123_456 : Int32); - /// ``` - public let toInt32 : (self : Int) -> Int32 = Prim.intToInt32; - - /// Converts a signed integer with infinite precision to a 64-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toInt64(123_456_789) == (123_456_789 : Int64); - /// ``` - public let toInt64 : (self : Int) -> Int64 = Prim.intToInt64; - - /// Converts an 8-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromInt8(123 : Int8) == 123; - /// ``` - public let fromInt8 : (x : Int8) -> Int = Prim.int8ToInt; - - /// Converts a 16-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromInt16(12_345 : Int16) == 12_345; - /// ``` - public let fromInt16 : (x : Int16) -> Int = Prim.int16ToInt; - - /// Converts a 32-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromInt32(123_456 : Int32) == 123_456; - /// ``` - public let fromInt32 : (x : Int32) -> Int = Prim.int32ToInt; - - /// Converts a 64-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromInt64(123_456_789 : Int64) == 123_456_789; - /// ``` - public let fromInt64 : (x : Int64) -> Int = Prim.int64ToInt; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.min(2, -3) == -3; - /// ``` - public func min(x : Int, y : Int) : Int { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.max(2, -3) == 2; - /// ``` - public func max(x : Int, y : Int) : Int { - if (x < y) { y } else { x } - }; - - /// Equality function for Int types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int = 1; - /// let b : Int = -1; - /// assert not Int.equal(a, b); - /// ``` - public func equal(x : Int, y : Int) : Bool { x == y }; - - /// Inequality function for Int types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int, y : Int) : Bool { x != y }; - - /// "Less than" function for Int types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int, y : Int) : Bool { x < y }; - - /// "Less than or equal" function for Int types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.lessOrEqual(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int, y : Int) : Bool { x <= y }; - - /// "Greater than" function for Int types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.greater(1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int, y : Int) : Bool { x > y }; - - /// "Greater than or equal" function for Int types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.greaterOrEqual(1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int, y : Int) : Bool { x >= y }; - - /// General-purpose comparison function for `Int`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3], Int.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int, y : Int) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x` . - /// - /// Example: - /// ```motoko include=import - /// assert Int.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int) : Int { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.add(1, -2) == -1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int.add) == -4; - /// ``` - public func add(x : Int, y : Int) : Int { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.sub(1, 2) == -1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int.sub) == 4; - /// ``` - public func sub(x : Int, y : Int) : Int { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.mul(-2, 3) == -6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int.mul) == 6; - /// ``` - public func mul(x : Int, y : Int) : Int { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int.div(6, -2) == -3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int, y : Int) : Int { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int.rem(6, -4) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int, y : Int) : Int { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps when `y` is negative or `y > 2 ** 32 - 1`. - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.pow(-2, 3) == -8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int, y : Int) : Int { x ** y }; - - /// Returns an iterator over the integers from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int, toExclusive : Int) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int { - if (n >= toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int` values from the first to second argument with an exclusive upper bound, - /// incrementing by the specified step size. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// // Positive step - /// let iter1 = Int.rangeBy(1, 7, 2); - /// assert iter1.next() == ?1; - /// assert iter1.next() == ?3; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// // Negative step - /// let iter2 = Int.rangeBy(7, 1, -2); - /// assert iter2.next() == ?7; - /// assert iter2.next() == ?5; - /// assert iter2.next() == ?3; - /// assert iter2.next() == null; - /// ``` - /// - /// If `step` is 0 or if the iteration would not progress towards the bound, returns an empty iterator. - public func rangeBy(fromInclusive : Int, toExclusive : Int, step : Int) : Iter.Iter { - if (step == 0) { - Iter.empty() - } else if (step > 0 and fromInclusive < toExclusive) { - object { - var n = fromInclusive; - public func next() : ?Int { - if (n >= toExclusive) { - null - } else { - let current = n; - n += step; - ?current - } - } - } - } else if (step < 0 and fromInclusive > toExclusive) { - object { - var n = fromInclusive; - public func next() : ?Int { - if (n <= toExclusive) { - null - } else { - let current = n; - n += step; - ?current - } - } - } - } else { - Iter.empty() - } - }; - - /// Returns an iterator over the integers from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int.rangeInclusive(3, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int, to : Int) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - public func next() : ?Int { - if (n > to) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over the integers from the first to second argument, inclusive, - /// incrementing by the specified step size. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// // Positive step - /// let iter1 = Int.rangeByInclusive(1, 7, 2); - /// assert iter1.next() == ?1; - /// assert iter1.next() == ?3; - /// assert iter1.next() == ?5; - /// assert iter1.next() == ?7; - /// assert iter1.next() == null; - /// - /// // Negative step - /// let iter2 = Int.rangeByInclusive(7, 1, -2); - /// assert iter2.next() == ?7; - /// assert iter2.next() == ?5; - /// assert iter2.next() == ?3; - /// assert iter2.next() == ?1; - /// assert iter2.next() == null; - /// ``` - /// - /// If `from == to`, return an iterator which only returns that value. - /// - /// Otherwise, if `step` is 0 or if the iteration would not progress towards the bound, returns an empty iterator. - public func rangeByInclusive(from : Int, to : Int, step : Int) : Iter.Iter { - if (from == to) { - Iter.singleton(from) - } else if (step == 0) { - Iter.empty() - } else if (step > 0 and from < to) { - object { - var n = from; - public func next() : ?Int { - if (n >= to + 1) { - null - } else { - let current = n; - n += step; - ?current - } - } - } - } else if (step < 0 and from > to) { - object { - var n = from; - public func next() : ?Int { - if (n + 1 <= to) { - null - } else { - let current = n; - n += step; - ?current - } - } - } - } else { - Iter.empty() - } - }; - -} diff --git a/.mops/core@2.4.0/src/Int16.mo b/.mops/core@2.4.0/src/Int16.mo deleted file mode 100644 index 40b3b6d..0000000 --- a/.mops/core@2.4.0/src/Int16.mo +++ /dev/null @@ -1,774 +0,0 @@ -/// Utility functions on 16-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int16 "mo:core/Int16"; -/// ``` - -import Int "Int"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 16-bit signed integers. - public type Int16 = Prim.Types.Int16; - - /// Minimum 16-bit integer value, `-2 ** 15`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.minValue == (-32_768 : Int16); - /// ``` - public let minValue : Int16 = -32_768; - - /// Maximum 16-bit integer value, `+2 ** 15 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.maxValue == (+32_767 : Int16); - /// ``` - public let maxValue : Int16 = 32_767; - - /// Converts a 16-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toInt(12_345) == (12_345 : Int); - /// ``` - public let toInt : (self : Int16) -> Int = Prim.int16ToInt; - - /// Converts a signed integer with infinite precision to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromInt(12_345) == (+12_345 : Int16); - /// ``` - public let fromInt : Int -> Int16 = Prim.intToInt16; - - /// Converts a signed integer with infinite precision to a 16-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromIntWrap(-12_345) == (-12_345 : Int); - /// ``` - public let fromIntWrap : Int -> Int16 = Prim.intToInt16Wrap; - - /// Converts a 8-bit signed integer to a 16-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromInt8(-123) == (-123 : Int16); - /// ``` - public let fromInt8 : Int8 -> Int16 = Prim.int8ToInt16; - - /// Converts a 16-bit signed integer to a 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toInt8(-123) == (-123 : Int8); - /// ``` - public let toInt8 : (self : Int16) -> Int8 = Prim.int16ToInt8; - - /// Converts a 32-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromInt32(-12_345) == (-12_345 : Int16); - /// ``` - public let fromInt32 : Int32 -> Int16 = Prim.int32ToInt16; - - /// Converts a 16-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toInt32(-12_345) == (-12_345 : Int32); - /// ``` - public let toInt32 : (self : Int16) -> Int32 = Prim.int16ToInt32; - - /// Converts a 64-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromInt64(-12_345) == (-12_345 : Int16); - /// ``` - public func fromInt64(x : Int64) : Int16 { - Prim.int32ToInt16(Prim.int64ToInt32(x)) - }; - - /// Converts a 16-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toInt64(-12_345) == (-12_345 : Int64); - /// ``` - public func toInt64(self : Int16) : Int64 { - Prim.int32ToInt64(Prim.int16ToInt32(self)) - }; - - /// Converts an unsigned 16-bit integer to a signed 16-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromNat16(12_345) == (+12_345 : Int16); - /// ``` - public let fromNat16 : Nat16 -> Int16 = Prim.nat16ToInt16; - - /// Converts a signed 16-bit integer to an unsigned 16-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toNat16(-1) == (65_535 : Nat16); // underflow - /// ``` - public let toNat16 : (self : Int16) -> Nat16 = Prim.int16ToNat16; - - /// Returns the Text representation of `x`. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toText(-12345) == "-12345"; - /// ``` - public func toText(self : Int16) : Text { - Int.toText(toInt(self)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 15` (the minimum `Int16` value). - /// - /// Example: - /// ```motoko include=import - /// assert Int16.abs(-12345) == +12_345; - /// ``` - public func abs(x : Int16) : Int16 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.min(+2, -3) == -3; - /// ``` - public func min(x : Int16, y : Int16) : Int16 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.max(+2, -3) == +2; - /// ``` - public func max(x : Int16, y : Int16) : Int16 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int16 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int16 = -123; - /// let b : Int16 = 123; - /// assert not Int16.equal(a, b); - /// ``` - public func equal(x : Int16, y : Int16) : Bool { x == y }; - - /// Inequality function for Int16 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int16, y : Int16) : Bool { x != y }; - - /// "Less than" function for Int16 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int16, y : Int16) : Bool { x < y }; - - /// "Less than or equal" function for Int16 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.lessOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int16, y : Int16) : Bool { x <= y }; - - /// "Greater than" function for Int16 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert not Int16.greater(-2, 1); - /// ``` - public func greater(x : Int16, y : Int16) : Bool { x > y }; - - /// "Greater than or equal" function for Int16 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.greaterOrEqual(-2, -2); - /// ``` - public func greaterOrEqual(x : Int16, y : Int16) : Bool { - x >= y - }; - - /// General-purpose comparison function for `Int16`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3] : [Int16], Int16.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int16, y : Int16) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 15)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int16) : Int16 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.add(100, 23) == +123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int16.add) == -4; - /// ``` - public func add(x : Int16, y : Int16) : Int16 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.sub(123, 100) == +23; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int16.sub) == 4; - /// ``` - public func sub(x : Int16, y : Int16) : Int16 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.mul(12, 10) == +120; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int16.mul) == 6; - /// ``` - public func mul(x : Int16, y : Int16) : Int16 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.div(123, 10) == +12; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int16, y : Int16) : Int16 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.rem(123, 10) == +3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int16, y : Int16) : Int16 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 16`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.pow(2, 10) == +1_024; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int16, y : Int16) : Int16 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitnot(-256 /* 0xff00 */) == +255 // 0xff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int16) : Int16 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitand(0x0fff, 0x00f0) == +240 // 0xf0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int16, y : Int16) : Int16 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitor(0x0f0f, 0x00f0) == +4_095 // 0x0fff; - /// ``` - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int16, y : Int16) : Int16 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitxor(0x0fff, 0x00f0) == +3_855 // 0x0f0f; - /// ``` - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int16, y : Int16) : Int16 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 16`, the semantics is the same as for `bitshiftLeft(x, y % 16)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitshiftLeft(1, 8) == +256 // 0x100 equivalent to `2 ** 8`.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int16, y : Int16) : Int16 { - x << y - }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 16`, the semantics is the same as for `bitshiftRight(x, y % 16)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitshiftRight(1024, 8) == +4 // equivalent to `1024 / (2 ** 8)`; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int16, y : Int16) : Int16 { - x >> y - }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 16`, the semantics is the same as for `bitrotLeft(x, y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitrotLeft(0x2001, 4) == +18 // 0x12.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int16, y : Int16) : Int16 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 16`, the semantics is the same as for `bitrotRight(x, y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitrotRight(0x2010, 8) == +4_128 // 0x01020.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int16, y : Int16) : Int16 { - x <>> y - }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 16`, the semantics is the same as for `bittest(x, p % 16)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bittest(128, 7); - /// ``` - public func bittest(x : Int16, p : Nat) : Bool { - Prim.btstInt16(x, Prim.intToInt16(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 16`, the semantics is the same as for `bitset(x, p % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitset(0, 7) == +128; - /// ``` - public func bitset(x : Int16, p : Nat) : Int16 { - x | (1 << Prim.intToInt16(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 16`, the semantics is the same as for `bitclear(x, p % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitclear(-1, 7) == -129; - /// ``` - public func bitclear(x : Int16, p : Nat) : Int16 { - x & ^(1 << Prim.intToInt16(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 16`, the semantics is the same as for `bitclear(x, p % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitflip(255, 7) == +127; - /// ``` - public func bitflip(x : Int16, p : Nat) : Int16 { - x ^ (1 << Prim.intToInt16(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitcountNonZero(0xff) == +8; - /// ``` - public let bitcountNonZero : (x : Int16) -> Int16 = Prim.popcntInt16; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitcountLeadingZero(0x80) == +8; - /// ``` - public let bitcountLeadingZero : (x : Int16) -> Int16 = Prim.clzInt16; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitcountTrailingZero(0x0100) == +8; - /// ``` - public let bitcountTrailingZero : (x : Int16) -> Int16 = Prim.ctzInt16; - - /// Returns the upper (i.e. most significant) and lower (least significant) byte of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.explode 0x77ee == (119, 238); - /// ``` - public let explode : (x : Int16) -> (msb : Nat8, lsb : Nat8) = Prim.explodeInt16; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.addWrap(2 ** 14, 2 ** 14) == -32_768; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int16, y : Int16) : Int16 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.subWrap(-2 ** 15, 1) == +32_767; // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int16, y : Int16) : Int16 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.mulWrap(2 ** 8, 2 ** 8) == 0; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int16, y : Int16) : Int16 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 16`. - /// - /// Example: - /// ```motoko include=import - /// - /// assert Int16.powWrap(2, 15) == -32_768; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int16, y : Int16) : Int16 { x **% y }; - - /// Returns an iterator over `Int16` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int16, toExclusive : Int16) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int16 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int16` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int16, to : Int16) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Int16 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Int16 values, from minValue to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.allValues(); - /// assert iter.next() == ?-32_768; - /// assert iter.next() == ?-32_767; - /// assert iter.next() == ?-32_766; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(minValue, maxValue) - }; - -} diff --git a/.mops/core@2.4.0/src/Int32.mo b/.mops/core@2.4.0/src/Int32.mo deleted file mode 100644 index 947b76b..0000000 --- a/.mops/core@2.4.0/src/Int32.mo +++ /dev/null @@ -1,787 +0,0 @@ -/// Utility functions on 32-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int32 "mo:core/Int32"; -/// ``` -import Int "Int"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 32-bit signed integers. - public type Int32 = Prim.Types.Int32; - - /// Minimum 32-bit integer value, `-2 ** 31`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.minValue == -2_147_483_648; - /// ``` - public let minValue : Int32 = -2_147_483_648; - - /// Maximum 32-bit integer value, `+2 ** 31 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.maxValue == +2_147_483_647; - /// ``` - public let maxValue : Int32 = 2_147_483_647; - - /// Converts a 32-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toInt(123_456) == (123_456 : Int); - /// ``` - public let toInt : (self : Int32) -> Int = Prim.int32ToInt; - - /// Converts a signed integer with infinite precision to a 32-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromInt(123_456) == (+123_456 : Int32); - /// ``` - public let fromInt : Int -> Int32 = Prim.intToInt32; - - /// Converts a signed integer with infinite precision to a 32-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromIntWrap(-123_456) == (-123_456 : Int); - /// ``` - public let fromIntWrap : Int -> Int32 = Prim.intToInt32Wrap; - - /// Converts a 16-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromInt16(-123) == (-123 : Int32); - /// ``` - public let fromInt16 : Int16 -> Int32 = Prim.int16ToInt32; - - /// Converts an 8-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromInt8(-123) == (-123 : Int32); - /// ``` - public func fromInt8(x : Int8) : Int32 { - Prim.int16ToInt32(Prim.int8ToInt16(x)) - }; - - /// Converts a 32-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toInt8(-123) == (-123 : Int8); - /// ``` - public func toInt8(self : Int32) : Int8 { - Prim.int16ToInt8(Prim.int32ToInt16(self)) - }; - - /// Converts a 32-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toInt16(-123) == (-123 : Int16); - /// ``` - public func toInt16(self : Int32) : Int16 { - Prim.int32ToInt16(self) - }; - - /// Converts a 64-bit signed integer to a 32-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromInt64(-123_456) == (-123_456 : Int32); - /// ``` - public let fromInt64 : Int64 -> Int32 = Prim.int64ToInt32; - - /// Converts a 32-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toInt64(-123_456) == (-123_456 : Int64); - /// ``` - public let toInt64 : (self : Int32) -> Int64 = Prim.int32ToInt64; - - /// Converts an unsigned 32-bit integer to a signed 32-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromNat32(123_456) == (+123_456 : Int32); - /// ``` - public let fromNat32 : Nat32 -> Int32 = Prim.nat32ToInt32; - - /// Converts a signed 32-bit integer to an unsigned 32-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toNat32(-1) == (4_294_967_295 : Nat32); // underflow - /// ``` - public let toNat32 : (self : Int32) -> Nat32 = Prim.int32ToNat32; - - /// Returns the Text representation of `x`. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toText(-123456) == "-123456"; - /// ``` - public func toText(self : Int32) : Text { - Int.toText(toInt(self)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 31` (the minimum `Int32` value). - /// - /// Example: - /// ```motoko include=import - /// assert Int32.abs(-123456) == +123_456; - /// ``` - public func abs(x : Int32) : Int32 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.min(+2, -3) == -3; - /// ``` - public func min(x : Int32, y : Int32) : Int32 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.max(+2, -3) == +2; - /// ``` - public func max(x : Int32, y : Int32) : Int32 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int32 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int32 = -123; - /// let b : Int32 = 123; - /// assert not Int32.equal(a, b); - /// ``` - public func equal(x : Int32, y : Int32) : Bool { x == y }; - - /// Inequality function for Int32 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int32, y : Int32) : Bool { x != y }; - - /// "Less than" function for Int32 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int32, y : Int32) : Bool { x < y }; - - /// "Less than or equal" function for Int32 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.lessOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int32, y : Int32) : Bool { x <= y }; - - /// "Greater than" function for Int32 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.greater(-2, -3); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int32, y : Int32) : Bool { x > y }; - - /// "Greater than or equal" function for Int32 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.greaterOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int32, y : Int32) : Bool { - x >= y - }; - - /// General-purpose comparison function for `Int32`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3] : [Int32], Int32.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int32, y : Int32) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 31)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int32) : Int32 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.add(100, 23) == +123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int32.add) == -4; - /// ``` - public func add(x : Int32, y : Int32) : Int32 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.sub(1234, 123) == +1_111; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int32.sub) == 4; - /// ``` - public func sub(x : Int32, y : Int32) : Int32 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.mul(123, 100) == +12_300; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int32.mul) == 6; - /// ``` - public func mul(x : Int32, y : Int32) : Int32 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.div(123, 10) == +12; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int32, y : Int32) : Int32 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.rem(123, 10) == +3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int32, y : Int32) : Int32 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 32`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.pow(2, 10) == +1_024; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int32, y : Int32) : Int32 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitnot(-256 /* 0xffff_ff00 */) == +255 // 0xff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int32) : Int32 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitand(0xffff, 0x00f0) == +240 // 0xf0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int32, y : Int32) : Int32 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitor(0xffff, 0x00f0) == +65_535 // 0xffff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int32, y : Int32) : Int32 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitxor(0xffff, 0x00f0) == +65_295 // 0xff0f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int32, y : Int32) : Int32 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 32`, the semantics is the same as for `bitshiftLeft(x, y % 32)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitshiftLeft(1, 8) == +256 // 0x100 equivalent to `2 ** 8`.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int32, y : Int32) : Int32 { - x << y - }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 32`, the semantics is the same as for `bitshiftRight(x, y % 32)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitshiftRight(1024, 8) == +4 // equivalent to `1024 / (2 ** 8)`; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int32, y : Int32) : Int32 { - x >> y - }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 32`, the semantics is the same as for `bitrotLeft(x, y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitrotLeft(0x2000_0001, 4) == +18 // 0x12.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int32, y : Int32) : Int32 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 32`, the semantics is the same as for `bitrotRight(x, y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitrotRight(0x0002_0001, 8) == +16_777_728 // 0x0100_0200.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int32, y : Int32) : Int32 { - x <>> y - }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 32`, the semantics is the same as for `bittest(x, p % 32)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bittest(128, 7); - /// ``` - public func bittest(x : Int32, p : Nat) : Bool { - Prim.btstInt32(x, Prim.intToInt32(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 32`, the semantics is the same as for `bitset(x, p % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitset(0, 7) == +128; - /// ``` - public func bitset(x : Int32, p : Nat) : Int32 { - x | (1 << Prim.intToInt32(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 32`, the semantics is the same as for `bitclear(x, p % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitclear(-1, 7) == -129; - /// ``` - public func bitclear(x : Int32, p : Nat) : Int32 { - x & ^(1 << Prim.intToInt32(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 32`, the semantics is the same as for `bitclear(x, p % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitflip(255, 7) == +127; - /// ``` - public func bitflip(x : Int32, p : Nat) : Int32 { - x ^ (1 << Prim.intToInt32(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitcountNonZero(0xffff) == +16; - /// ``` - public let bitcountNonZero : (x : Int32) -> Int32 = Prim.popcntInt32; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitcountLeadingZero(0x8000) == +16; - /// ``` - public let bitcountLeadingZero : (x : Int32) -> Int32 = Prim.clzInt32; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitcountTrailingZero(0x0201_0000) == +16; - /// ``` - public let bitcountTrailingZero : (x : Int32) -> Int32 = Prim.ctzInt32; - - /// Returns the upper (i.e. most significant), lower (least significant) - /// and in-between bytes of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.explode 0x66885511 == (102, 136, 85, 17); - /// ``` - public let explode : (x : Int32) -> (msb : Nat8, Nat8, Nat8, lsb : Nat8) = Prim.explodeInt32; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.addWrap(2 ** 30, 2 ** 30) == -2_147_483_648; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int32, y : Int32) : Int32 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.subWrap(-2 ** 31, 1) == +2_147_483_647; // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int32, y : Int32) : Int32 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.mulWrap(2 ** 16, 2 ** 16) == 0; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int32, y : Int32) : Int32 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 32`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.powWrap(2, 31) == -2_147_483_648; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int32, y : Int32) : Int32 { x **% y }; - - /// Returns an iterator over `Int32` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int32, toExclusive : Int32) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int32 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int32` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int32, to : Int32) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Int32 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Int32 values, from minValue to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.allValues(); - /// assert iter.next() == ?-2_147_483_648; - /// assert iter.next() == ?-2_147_483_647; - /// assert iter.next() == ?-2_147_483_646; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(minValue, maxValue) - }; - -} diff --git a/.mops/core@2.4.0/src/Int64.mo b/.mops/core@2.4.0/src/Int64.mo deleted file mode 100644 index 95f5647..0000000 --- a/.mops/core@2.4.0/src/Int64.mo +++ /dev/null @@ -1,796 +0,0 @@ -/// Utility functions on 64-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int64 "mo:core/Int64"; -/// ``` - -import Int "Int"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 64-bit signed integers. - public type Int64 = Prim.Types.Int64; - - /// Minimum 64-bit integer value, `-2 ** 63`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.minValue == -9_223_372_036_854_775_808; - /// ``` - public let minValue : Int64 = -9_223_372_036_854_775_808; - - /// Maximum 64-bit integer value, `+2 ** 63 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.maxValue == +9_223_372_036_854_775_807; - /// ``` - public let maxValue : Int64 = 9_223_372_036_854_775_807; - - /// Converts a 64-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toInt(123_456) == (123_456 : Int); - /// ``` - public let toInt : (self : Int64) -> Int = Prim.int64ToInt; - - /// Converts a signed integer with infinite precision to a 64-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromInt(123_456) == (+123_456 : Int64); - /// ``` - public let fromInt : (x : Int) -> Int64 = Prim.intToInt64; - - /// Converts a 32-bit signed integer to a 64-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromInt32(-123_456) == (-123_456 : Int64); - /// ``` - public let fromInt32 : (x : Int32) -> Int64 = Prim.int32ToInt64; - - /// Converts a 16-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromInt16(-123) == (-123 : Int64); - /// ``` - public func fromInt16(x : Int16) : Int64 { - Prim.int32ToInt64(Prim.int16ToInt32(x)) - }; - - /// Converts an 8-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromInt8(-123) == (-123 : Int64); - /// ``` - public func fromInt8(x : Int8) : Int64 { - Prim.int32ToInt64(Prim.int16ToInt32(Prim.int8ToInt16(x))) - }; - - /// Converts a 64-bit signed integer to a 32-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toInt32(-123_456) == (-123_456 : Int32); - /// ``` - public func toInt32(self : Int64) : Int32 { - Prim.int64ToInt32(self) - }; - - /// Converts a 64-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toInt16(-123) == (-123 : Int16); - /// ``` - public func toInt16(self : Int64) : Int16 { - Prim.int32ToInt16(Prim.int64ToInt32(self)) - }; - - /// Converts a 64-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toInt8(-123) == (-123 : Int8); - /// ``` - public func toInt8(self : Int64) : Int8 { - Prim.int16ToInt8(Prim.int32ToInt16(Prim.int64ToInt32(self))) - }; - - /// Converts a signed integer with infinite precision to a 64-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromIntWrap(-123_456) == (-123_456 : Int64); - /// ``` - public let fromIntWrap : Int -> Int64 = Prim.intToInt64Wrap; - - /// Converts an unsigned 64-bit integer to a signed 64-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromNat64(123_456) == (+123_456 : Int64); - /// ``` - public let fromNat64 : Nat64 -> Int64 = Prim.nat64ToInt64; - - /// Converts a signed 64-bit integer to an unsigned 64-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toNat64(-1) == (18_446_744_073_709_551_615 : Nat64); // underflow - /// ``` - public let toNat64 : (self : Int64) -> Nat64 = Prim.int64ToNat64; - - /// Returns the Text representation of `x`. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toText(-123456) == "-123456"; - /// ``` - public func toText(self : Int64) : Text { - Int.toText(toInt(self)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 63` (the minimum `Int64` value). - /// - /// Example: - /// ```motoko include=import - /// assert Int64.abs(-123456) == +123_456; - /// ``` - public func abs(x : Int64) : Int64 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.min(+2, -3) == -3; - /// ``` - public func min(x : Int64, y : Int64) : Int64 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.max(+2, -3) == +2; - /// ``` - public func max(x : Int64, y : Int64) : Int64 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int64 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int64 = -123; - /// let b : Int64 = 123; - /// assert not Int64.equal(a, b); - /// ``` - public func equal(x : Int64, y : Int64) : Bool { x == y }; - - /// Inequality function for Int64 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int64, y : Int64) : Bool { x != y }; - - /// "Less than" function for Int64 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int64, y : Int64) : Bool { x < y }; - - /// "Less than or equal" function for Int64 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.lessOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int64, y : Int64) : Bool { x <= y }; - - /// "Greater than" function for Int64 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.greater(-2, -3); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int64, y : Int64) : Bool { x > y }; - - /// "Greater than or equal" function for Int64 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.greaterOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int64, y : Int64) : Bool { - x >= y - }; - - /// General-purpose comparison function for `Int64`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3] : [Int64], Int64.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int64, y : Int64) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 63)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int64) : Int64 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.add(1234, 123) == +1_357; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int64.add) == -4; - /// ``` - public func add(x : Int64, y : Int64) : Int64 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.sub(123, 100) == +23; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int64.sub) == 4; - /// ``` - public func sub(x : Int64, y : Int64) : Int64 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.mul(123, 10) == +1_230; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int64.mul) == 6; - /// ``` - public func mul(x : Int64, y : Int64) : Int64 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.div(123, 10) == +12; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int64, y : Int64) : Int64 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.rem(123, 10) == +3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int64, y : Int64) : Int64 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 64`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.pow(2, 10) == +1_024; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int64, y : Int64) : Int64 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitnot(-256 /* 0xffff_ffff_ffff_ff00 */) == +255 // 0xff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int64) : Int64 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitand(0xffff, 0x00f0) == +240 // 0xf0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int64, y : Int64) : Int64 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitor(0xffff, 0x00f0) == +65_535 // 0xffff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int64, y : Int64) : Int64 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitxor(0xffff, 0x00f0) == +65_295 // 0xff0f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int64, y : Int64) : Int64 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 64`, the semantics is the same as for `bitshiftLeft(x, y % 64)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitshiftLeft(1, 8) == +256 // 0x100 equivalent to `2 ** 8`.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int64, y : Int64) : Int64 { - x << y - }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 64`, the semantics is the same as for `bitshiftRight(x, y % 64)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitshiftRight(1024, 8) == +4 // equivalent to `1024 / (2 ** 8)`; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int64, y : Int64) : Int64 { - x >> y - }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 64`, the semantics is the same as for `bitrotLeft(x, y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// - /// assert Int64.bitrotLeft(0x2000_0000_0000_0001, 4) == +18 // 0x12.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int64, y : Int64) : Int64 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 64`, the semantics is the same as for `bitrotRight(x, y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitrotRight(0x0002_0000_0000_0001, 48) == +65538 // 0x1_0002.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int64, y : Int64) : Int64 { - x <>> y - }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 64`, the semantics is the same as for `bittest(x, p % 64)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bittest(128, 7); - /// ``` - public func bittest(x : Int64, p : Nat) : Bool { - Prim.btstInt64(x, Prim.intToInt64(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 64`, the semantics is the same as for `bitset(x, p % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitset(0, 7) == +128; - /// ``` - public func bitset(x : Int64, p : Nat) : Int64 { - x | (1 << Prim.intToInt64(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 64`, the semantics is the same as for `bitclear(x, p % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitclear(-1, 7) == -129; - /// ``` - public func bitclear(x : Int64, p : Nat) : Int64 { - x & ^(1 << Prim.intToInt64(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 64`, the semantics is the same as for `bitclear(x, p % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitflip(255, 7) == +127; - /// ``` - public func bitflip(x : Int64, p : Nat) : Int64 { - x ^ (1 << Prim.intToInt64(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitcountNonZero(0xffff) == +16; - /// ``` - public let bitcountNonZero : (x : Int64) -> Int64 = Prim.popcntInt64; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitcountLeadingZero(0x8000_0000) == +32; - /// ``` - public let bitcountLeadingZero : (x : Int64) -> Int64 = Prim.clzInt64; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitcountTrailingZero(0x0201_0000) == +16; - /// ``` - public let bitcountTrailingZero : (x : Int64) -> Int64 = Prim.ctzInt64; - - /// Returns the upper (i.e. most significant), lower (least significant) - /// and in-between bytes of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.explode 0x33772266aa885511 == (51, 119, 34, 102, 170, 136, 85, 17); - /// ``` - public let explode : (x : Int64) -> (msb : Nat8, Nat8, Nat8, Nat8, Nat8, Nat8, Nat8, lsb : Nat8) = Prim.explodeInt64; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.addWrap(2 ** 62, 2 ** 62) == -9_223_372_036_854_775_808; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int64, y : Int64) : Int64 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.subWrap(-2 ** 63, 1) == +9_223_372_036_854_775_807; // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int64, y : Int64) : Int64 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.mulWrap(2 ** 32, 2 ** 32) == 0; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int64, y : Int64) : Int64 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 64`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.powWrap(2, 63) == -9_223_372_036_854_775_808; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int64, y : Int64) : Int64 { x **% y }; - - /// Returns an iterator over `Int64` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int64, toExclusive : Int64) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int64 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int64` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int64, to : Int64) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Int64 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Int64 values, from minValue to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.allValues(); - /// assert iter.next() == ?-9_223_372_036_854_775_808; - /// assert iter.next() == ?-9_223_372_036_854_775_807; - /// assert iter.next() == ?-9_223_372_036_854_775_806; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(minValue, maxValue) - }; - -} diff --git a/.mops/core@2.4.0/src/Int8.mo b/.mops/core@2.4.0/src/Int8.mo deleted file mode 100644 index ffde265..0000000 --- a/.mops/core@2.4.0/src/Int8.mo +++ /dev/null @@ -1,771 +0,0 @@ -/// Utility functions on 8-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int8 "mo:core/Int8"; -/// ``` -import Int "Int"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 8-bit signed integers. - public type Int8 = Prim.Types.Int8; - - /// Minimum 8-bit integer value, `-2 ** 7`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.minValue == -128; - /// ``` - public let minValue : Int8 = -128; - - /// Maximum 8-bit integer value, `+2 ** 7 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.maxValue == +127; - /// ``` - public let maxValue : Int8 = 127; - - /// Converts an 8-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toInt(123) == (123 : Int); - /// ``` - public let toInt : (self : Int8) -> Int = Prim.int8ToInt; - - /// Converts a signed integer with infinite precision to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromInt(123) == (+123 : Int8); - /// ``` - public let fromInt : Int -> Int8 = Prim.intToInt8; - - /// Converts a signed integer with infinite precision to an 8-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromIntWrap(-123) == (-123 : Int8); - /// ``` - public let fromIntWrap : Int -> Int8 = Prim.intToInt8Wrap; - - /// Converts a 16-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromInt16(123) == (+123 : Int8); - /// ``` - public let fromInt16 : Int16 -> Int8 = Prim.int16ToInt8; - - /// Converts an 8-bit signed integer to a 16-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toInt16(123) == (+123 : Int16); - /// ``` - public let toInt16 : (self : Int8) -> Int16 = Prim.int8ToInt16; - - /// Converts a 32-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromInt32(123) == (+123 : Int8); - /// ``` - public func fromInt32(x : Int32) : Int8 { - Prim.int16ToInt8(Prim.int32ToInt16(x)) - }; - - /// Converts an 8-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toInt32(123) == (+123 : Int32); - /// ``` - public func toInt32(self : Int8) : Int32 { - Prim.int16ToInt32(Prim.int8ToInt16(self)) - }; - - /// Converts a 64-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromInt64(123) == (+123 : Int8); - /// ``` - public func fromInt64(x : Int64) : Int8 { - Prim.int16ToInt8(Prim.int32ToInt16(Prim.int64ToInt32(x))) - }; - - /// Converts an 8-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toInt64(123) == (+123 : Int64); - /// ``` - public func toInt64(self : Int8) : Int64 { - Prim.int32ToInt64(Prim.int16ToInt32(Prim.int8ToInt16(self))) - }; - - /// Converts an unsigned 8-bit integer to a signed 8-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromNat8(123) == (+123 : Int8); - /// ``` - public let fromNat8 : Nat8 -> Int8 = Prim.nat8ToInt8; - - /// Converts a signed 8-bit integer to an unsigned 8-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toNat8(-1) == (255 : Nat8); // underflow - /// ``` - public let toNat8 : (self : Int8) -> Nat8 = Prim.int8ToNat8; - - /// Converts an integer number to its textual representation. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toText(-123) == "-123"; - /// ``` - public func toText(self : Int8) : Text { - Int.toText(toInt(self)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 7` (the minimum `Int8` value). - /// - /// Example: - /// ```motoko include=import - /// assert Int8.abs(-123) == +123; - /// ``` - public func abs(x : Int8) : Int8 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.min(+2, -3) == -3; - /// ``` - public func min(x : Int8, y : Int8) : Int8 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.max(+2, -3) == +2; - /// ``` - public func max(x : Int8, y : Int8) : Int8 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int8 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int8 = -123; - /// let b : Int8 = 123; - /// assert not Int8.equal(a, b); - /// ``` - public func equal(x : Int8, y : Int8) : Bool { x == y }; - - /// Inequality function for Int8 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int8, y : Int8) : Bool { x != y }; - - /// "Less than" function for Int8 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int8, y : Int8) : Bool { x < y }; - - /// "Less than or equal" function for Int8 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.lessOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int8, y : Int8) : Bool { x <= y }; - - /// "Greater than" function for Int8 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.greater(-2, -3); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int8, y : Int8) : Bool { x > y }; - - /// "Greater than or equal" function for Int8 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.greaterOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int8, y : Int8) : Bool { x >= y }; - - /// General-purpose comparison function for `Int8`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3] : [Int8], Int8.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int8, y : Int8) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 7)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int8) : Int8 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.add(100, 23) == +123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int8.add) == -4; - /// ``` - public func add(x : Int8, y : Int8) : Int8 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.sub(123, 23) == +100; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int8.sub) == 4; - /// ``` - public func sub(x : Int8, y : Int8) : Int8 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.mul(12, 10) == +120; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int8.mul) == 6; - /// ``` - public func mul(x : Int8, y : Int8) : Int8 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.div(123, 10) == +12; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int8, y : Int8) : Int8 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.rem(123, 10) == +3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int8, y : Int8) : Int8 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 8`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.pow(2, 6) == +64; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int8, y : Int8) : Int8 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitnot(-16 /* 0xf0 */) == +15 // 0x0f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int8) : Int8 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitand(0x1f, 0x70) == +16 // 0x10; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int8, y : Int8) : Int8 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitor(0x0f, 0x70) == +127 // 0x7f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int8, y : Int8) : Int8 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitxor(0x70, 0x7f) == +15 // 0x0f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int8, y : Int8) : Int8 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 8`, the semantics is the same as for `bitshiftLeft(x, y % 8)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitshiftLeft(1, 4) == +16 // 0x10 equivalent to `2 ** 4`.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int8, y : Int8) : Int8 { x << y }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 8`, the semantics is the same as for `bitshiftRight(x, y % 8)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitshiftRight(64, 4) == +4 // equivalent to `64 / (2 ** 4)`; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int8, y : Int8) : Int8 { x >> y }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 8`, the semantics is the same as for `bitrotLeft(x, y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitrotLeft(0x11 /* 0b0001_0001 */, 2) == +68 // 0b0100_0100 == 0x44.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int8, y : Int8) : Int8 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 8`, the semantics is the same as for `bitrotRight(x, y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitrotRight(0x11 /* 0b0001_0001 */, 1) == -120 // 0b1000_1000 == 0x88.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int8, y : Int8) : Int8 { x <>> y }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 8`, the semantics is the same as for `bittest(x, p % 8)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bittest(64, 6); - /// ``` - public func bittest(x : Int8, p : Nat) : Bool { - Prim.btstInt8(x, Prim.intToInt8(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 8`, the semantics is the same as for `bitset(x, p % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitset(0, 6) == +64; - /// ``` - public func bitset(x : Int8, p : Nat) : Int8 { - x | (1 << Prim.intToInt8(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 8`, the semantics is the same as for `bitclear(x, p % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitclear(-1, 6) == -65; - /// ``` - public func bitclear(x : Int8, p : Nat) : Int8 { - x & ^(1 << Prim.intToInt8(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 8`, the semantics is the same as for `bitclear(x, p % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitflip(127, 6) == +63; - /// ``` - public func bitflip(x : Int8, p : Nat) : Int8 { - x ^ (1 << Prim.intToInt8(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitcountNonZero(0x0f) == +4; - /// ``` - public let bitcountNonZero : (x : Int8) -> Int8 = Prim.popcntInt8; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitcountLeadingZero(0x08) == +4; - /// ``` - public let bitcountLeadingZero : (x : Int8) -> Int8 = Prim.clzInt8; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitcountTrailingZero(0x10) == +4; - /// ``` - public let bitcountTrailingZero : (x : Int8) -> Int8 = Prim.ctzInt8; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.addWrap(2 ** 6, 2 ** 6) == -128; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int8, y : Int8) : Int8 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.subWrap(-2 ** 7, 1) == +127; // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int8, y : Int8) : Int8 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.mulWrap(2 ** 4, 2 ** 4) == 0; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int8, y : Int8) : Int8 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 8`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.powWrap(2, 7) == -128; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int8, y : Int8) : Int8 { x **% y }; - - /// Returns an iterator over `Int8` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int8, toExclusive : Int8) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int8 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int8` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int8, to : Int8) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Int8 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Int8 values, from minValue to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.allValues(); - /// assert iter.next() == ?-128; - /// assert iter.next() == ?-127; - /// assert iter.next() == ?-126; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(minValue, maxValue) - }; - -} diff --git a/.mops/core@2.4.0/src/InternetComputer.mo b/.mops/core@2.4.0/src/InternetComputer.mo deleted file mode 100644 index 1a6d618..0000000 --- a/.mops/core@2.4.0/src/InternetComputer.mo +++ /dev/null @@ -1,101 +0,0 @@ -/// Low-level interface to the Internet Computer. - -import Prim "mo:⛔"; - -module { - - /// Calls `canister`'s update or query function, `name`, with the binary contents of `data` as IC argument. - /// Returns the response to the call, an IC _reply_ or _reject_, as a Motoko future: - /// - /// * The message data of an IC reply determines the binary contents of `reply`. - /// * The error code and textual message data of an IC reject determines the future's `Error` value. - /// - /// Note: `call` is an asynchronous function and can only be applied in an asynchronous context. - /// - /// Example: - /// ```motoko no-repl - /// import IC "mo:core/InternetComputer"; - /// import Principal "mo:core/Principal"; - /// - /// persistent actor { - /// type OutputType = { decimals : Nat32 }; - /// - /// public func example() : async ?OutputType { - /// let ledger = Principal.fromText("ryjl3-tyaaa-aaaaa-aaaba-cai"); - /// let method = "decimals"; - /// let input = (); - /// - /// let rawReply = await IC.call(ledger, method, to_candid (input)); // serialized Candid - /// let output : ?OutputType = from_candid (rawReply); - /// assert output == ?{ decimals = 8 }; - /// output - /// } - /// } - /// ``` - /// - /// [Learn more about Candid serialization](https://internetcomputer.org/docs/motoko/language-manual#candid-serialization) - public let call : (canister : Principal, name : Text, data : Blob) -> async (reply : Blob) = Prim.call_raw; - - /// `isReplicated` is true for update messages and for queries that passed through consensus. - public let isReplicated : () -> Bool = Prim.isReplicatedExecution; - - /// Given computation, `comp`, counts the number of actual and (for IC system calls) notional WebAssembly - /// instructions performed during the execution of `comp()`. - /// - /// More precisely, returns the difference between the state of the IC instruction counter (_performance counter_ `0`) before and after executing `comp()` - /// (see [Performance Counter](https://internetcomputer.org/docs/current/references/ic-interface-spec#system-api-performance-counter)). - /// - /// NB: `countInstructions(comp)` will _not_ account for any deferred garbage collection costs incurred by `comp()`. - /// - /// Example: - /// ```motoko no-repl - /// import IC "mo:core/InternetComputer"; - /// - /// let count = IC.countInstructions(func() { - /// // ... - /// }); - /// ``` - public func countInstructions(comp : () -> ()) : Nat64 { - let init = Prim.performanceCounter(0); - let pre = Prim.performanceCounter(0); - comp(); - let post = Prim.performanceCounter(0); - // performance_counter costs around 200 extra instructions; we perform an empty measurement to decide the overhead - let overhead = pre - init; - post - pre - overhead - }; - - /// Returns the current value of IC _performance counter_ `counter`. - /// - /// * Counter `0` is the _current execution instruction counter_, counting instructions only since the beginning of the current IC message. - /// This counter is reset to value `0` on shared function entry and every `await`. - /// It is therefore only suitable for measuring the cost of synchronous code. - /// - /// * Counter `1` is the _call context instruction counter_ for the current shared function call. - /// For replicated message executing, this excludes the cost of nested IC calls (even to the current canister). - /// For non-replicated messages, such as composite queries, it includes the cost of nested calls. - /// The current value of this counter is preserved across `awaits` (unlike counter `0`). - /// - /// * The function (currently) traps if `counter` >= 2. - /// - /// Consult [Performance Counter](https://internetcomputer.org/docs/current/references/ic-interface-spec#system-api-performance-counter) for details. - /// - /// Example: - /// ```motoko no-repl - /// import IC "mo:core/InternetComputer"; - /// - /// let c1 = IC.performanceCounter(1); - /// // ... - /// let diff : Nat64 = IC.performanceCounter(1) - c1; - /// ``` - public let performanceCounter : (counter : Nat32) -> (value : Nat64) = Prim.performanceCounter; - - /// Returns the time (in nanoseconds from the epoch start) by when the update message should - /// reply to the best effort message so that it can be received by the requesting canister. - /// Queries and unbounded-time update messages return null. - public func replyDeadline() : ?Nat { - let raw = Prim.replyDeadline(); - if (raw == 0) null else ?Prim.nat64ToNat(raw) - }; - -} diff --git a/.mops/core@2.4.0/src/Iter.mo b/.mops/core@2.4.0/src/Iter.mo deleted file mode 100644 index c78b99c..0000000 --- a/.mops/core@2.4.0/src/Iter.mo +++ /dev/null @@ -1,869 +0,0 @@ -/// Utilities for `Iter` (iterator) values. -/// -/// Iterators are a way to represent sequences of values that can be lazily produced. -/// They can be used to: -/// - Iterate over collections. -/// - Represent collections that are too large to fit in memory or that are produced incrementally. -/// - Transform collections without creating intermediate collections. -/// -/// Iterators are inherently stateful. Calling `next` "consumes" a value from -/// the Iterator that cannot be put back, so keep that in mind when sharing -/// iterators between consumers. -/// -/// ```motoko name=import -/// import Iter "mo:core/Iter"; -/// ``` -/// -/// -/// An iterator can be iterated over using a `for` loop: -/// ```motoko -/// let iter = [1, 2, 3].values(); -/// for (x in iter) { -/// // do something with x... -/// } -/// ``` -/// -/// Iterators can be: -/// - created from other collections (e.g. using `values` or `keys` function on a `Map`) or from scratch (e.g. using `empty` or `singleton`). -/// - transformed using `map`, `filter`, `concat`, etc. Which can be used to compose several transformations together without materializing intermediate collections. -/// - consumed using `forEach`, `size`, `toArray`, etc. -/// - combined using `concat`. - -import Prim "mo:prim"; - -import Array "Array"; -import Order "Order"; -import Runtime "Runtime"; -import Types "Types"; -import VarArray "VarArray"; - -module { - - /// An iterator that produces values of type `T`. Calling `next` returns - /// `null` when iteration is finished. - /// - /// Iterators are inherently stateful. Calling `next` "consumes" a value from - /// the Iterator that cannot be put back, so keep that in mind when sharing - /// iterators between consumers. - /// - /// An iterator `i` can be iterated over using - /// ```motoko - /// let iter = [1, 2, 3].values(); - /// for (x in iter) { - /// // do something with x... - /// } - /// ``` - public type Iter = Types.Iter; - - /// Creates an empty iterator. - /// - /// ```motoko include=import - /// for (x in Iter.empty()) - /// assert false; // This loop body will never run - /// ``` - public func empty() : Iter { - object { - public func next() : ?T { - null - } - } - }; - - /// Creates an iterator that produces a single value. - /// - /// ```motoko include=import - /// var sum = 0; - /// for (x in Iter.singleton(3)) - /// sum += x; - /// assert sum == 3; - /// ``` - public func singleton(value : T) : Iter { - object { - var state = ?value; - public func next() : ?T { - switch state { - case null null; - case some { - state := null; - some - } - } - } - } - }; - - /// Calls a function `f` on every value produced by an iterator and discards - /// the results. If you're looking to keep these results use `map` instead. - /// - /// ```motoko include=import - /// var sum = 0; - /// Iter.forEach([1, 2, 3].values(), func(x) { - /// sum += x; - /// }); - /// assert sum == 6; - /// ``` - public func forEach( - self : Iter, - f : (T) -> () - ) { - label l loop { - switch (self.next()) { - case (?next) { - f(next) - }; - case (null) { - break l - } - } - } - }; - - /// Takes an iterator and returns a new iterator that pairs each element with its index. - /// The index starts at 0 and increments by 1 for each element. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray(["A", "B", "C"]); - /// let enumerated = Iter.enumerate(iter); - /// let result = Iter.toArray(enumerated); - /// assert result == [(0, "A"), (1, "B"), (2, "C")]; - /// ``` - public func enumerate(self : Iter) : Iter<(Nat, T)> { - object { - var i = 0; - public func next() : ?(Nat, T) { - switch (self.next()) { - case (?x) { - let current = (i, x); - i += 1; - ?current - }; - case null { null } - } - } - } - }; - - /// Creates a new iterator that yields every nth element from the original iterator. - /// If `interval` is 0, returns an empty iterator. If `interval` is 1, returns the original iterator. - /// For any other positive interval, returns an iterator that skips `interval - 1` elements after each yielded element. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5, 6]); - /// let steppedIter = Iter.step(iter, 2); // Take every 2nd element - /// assert ?1 == steppedIter.next(); - /// assert ?3 == steppedIter.next(); - /// assert ?5 == steppedIter.next(); - /// assert null == steppedIter.next(); - /// ``` - public func step(self : Iter, n : Nat) : Iter { - if (n == 0) { - empty() - } else if (n == 1) { - self - } else { - object { - public func next() : ?T { - let item = self.next(); - var i = 1; - while (i < n) { - ignore self.next(); - i += 1 - }; - item - } - } - } - }; - - /// Consumes an iterator and counts how many elements were produced (discarding them in the process). - /// ```motoko include=import - /// let iter = [1, 2, 3].values(); - /// assert 3 == Iter.size(iter); - /// ``` - public func size(self : Iter) : Nat { - var len = 0; - forEach(self, func(x) { len += 1 }); - len - }; - - /// Takes a function and an iterator and returns a new iterator that lazily applies - /// the function to every element produced by the argument iterator. - /// ```motoko include=import - /// let iter = [1, 2, 3].values(); - /// let mappedIter = Iter.map(iter, func (x) = x * 2); - /// let result = Iter.toArray(mappedIter); - /// assert result == [2, 4, 6]; - /// ``` - public func map(self : Iter, f : T -> R) : Iter = object { - public func next() : ?R { - switch (self.next()) { - case (?next) { - ?f(next) - }; - case (null) { - null - } - } - } - }; - - /// Creates a new iterator that only includes elements from the original iterator - /// for which the predicate function returns true. - /// - /// ```motoko include=import - /// let iter = [1, 2, 3, 4, 5].values(); - /// let evenNumbers = Iter.filter(iter, func (x) = x % 2 == 0); - /// let result = Iter.toArray(evenNumbers); - /// assert result == [2, 4]; - /// ``` - public func filter(self : Iter, f : T -> Bool) : Iter = object { - public func next() : ?T { - loop { - let ?x = self.next() else return null; - if (f x) return ?x - }; - null - } - }; - - /// Creates a new iterator by applying a transformation function to each element - /// of the original iterator. Elements for which the function returns null are - /// excluded from the result. - /// - /// ```motoko include=import - /// let iter = [1, 2, 3].values(); - /// let evenNumbers = Iter.filterMap(iter, func (x) = if (x % 2 == 0) ?x else null); - /// let result = Iter.toArray(evenNumbers); - /// assert result == [2]; - /// ``` - public func filterMap(self : Iter, f : T -> ?R) : Iter = object { - public func next() : ?R { - loop { - let ?x = self.next() else return null; - switch (f x) { - case (?r) return ?r; - case null {} // continue - } - } - } - }; - - /// Flattens an iterator of iterators into a single iterator by concatenating the inner iterators. - /// - /// Possible optimization: Use `flatMap` when you need to transform elements before calling `flatten`. Example: use `flatMap(...)` instead of `flatten(map(...))`. - /// ```motoko include=import - /// let iter = Iter.flatten([[1, 2].values(), [3].values(), [4, 5, 6].values()].values()); - /// let result = Iter.toArray(iter); - /// assert result == [1, 2, 3, 4, 5, 6]; - /// ``` - public func flatten(self : Iter>) : Iter = object { - var current : Iter = empty(); - public func next() : ?T { - loop { - switch (current.next()) { - case (?x) return ?x; - case null { - let ?next = self.next() else return null; - current := next - } - } - } - } - }; - - /// Transforms every element of an iterator into an iterator and concatenates the results. - /// ```motoko include=import - /// let iter = Iter.flatMap([1, 3, 5].values(), func (x) = [x, x + 1].values()); - /// let result = Iter.toArray(iter); - /// assert result == [1, 2, 3, 4, 5, 6]; - /// ``` - public func flatMap(self : Iter, f : T -> Iter) : Iter = object { - var current : Iter = empty(); - public func next() : ?R { - loop { - switch (current.next()) { - case (?x) return ?x; - case null { - let ?next = self.next() else return null; - current := f(next) - } - } - } - } - }; - - /// Returns a new iterator that yields at most, first `n` elements from the original iterator. - /// After `n` elements have been produced or the original iterator is exhausted, - /// subsequent calls to `next()` will return `null`. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5]); - /// let first3 = Iter.take(iter, 3); - /// let result = Iter.toArray(first3); - /// assert result == [1, 2, 3]; - /// ``` - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3]); - /// let first5 = Iter.take(iter, 5); - /// let result = Iter.toArray(first5); - /// assert result == [1, 2, 3]; // only 3 elements in the original iterator - /// ``` - public func take(self : Iter, n : Nat) : Iter = object { - var remaining = n; - public func next() : ?T { - if (remaining == 0) return null; - remaining -= 1; - self.next() - } - }; - - /// Returns a new iterator that yields elements from the original iterator until the predicate function returns false. - /// The first element for which the predicate returns false is not included in the result. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5, 4, 3, 2, 1]); - /// let result = Iter.takeWhile(iter, func (x) = x < 4); - /// let array = Iter.toArray(result); - /// assert array == [1, 2, 3]; // note the difference between `takeWhile` and `filter` - /// ``` - public func takeWhile(self : Iter, f : T -> Bool) : Iter = object { - var done = false; - public func next() : ?T { - if done return null; - let ?x = self.next() else return null; - if (f x) return ?x; - done := true; - null - } - }; - - /// Returns a new iterator that skips the first `n` elements from the original iterator. - /// If the original iterator has fewer than `n` elements, the result will be an empty iterator. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5]); - /// let skipped = Iter.drop(iter, 3); - /// let result = Iter.toArray(skipped); - /// assert result == [4, 5]; - /// ``` - public func drop(self : Iter, n : Nat) : Iter = object { - var remaining = n; - public func next() : ?T { - while (remaining > 0) { - let ?_ = self.next() else return null; - remaining -= 1 - }; - self.next() - } - }; - - /// Returns a new iterator that skips elements from the original iterator until the predicate function returns false. - /// The first element for which the predicate returns false is the first element produced by the new iterator. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5, 4, 3, 2, 1]); - /// let result = Iter.dropWhile(iter, func (x) = x < 4); - /// let array = Iter.toArray(result); - /// assert array == [4, 5, 4, 3, 2, 1]; // notice that `takeWhile` and `dropWhile` are complementary - /// ``` - public func dropWhile(self : Iter, f : T -> Bool) : Iter = object { - var dropping = true; - public func next() : ?T { - while dropping { - let ?x = self.next() else return null; - if (not f x) { - dropping := false; - return ?x - } - }; - self.next() - } - }; - - /// Zips two iterators into a single iterator that produces pairs of elements. - /// The resulting iterator will stop producing elements when either of the input iterators is exhausted. - /// - /// ```motoko include=import - /// let iter1 = [1, 2, 3].values(); - /// let iter2 = ["A", "B"].values(); - /// let zipped = Iter.zip(iter1, iter2); - /// let result = Iter.toArray(zipped); - /// assert result == [(1, "A"), (2, "B")]; // note that the third element from iter1 is not included, because iter2 is exhausted - /// ``` - public func zip(self : Iter, other : Iter) : Iter<(A, B)> = object { - public func next() : ?(A, B) { - let ?x = self.next() else return null; - let ?y = other.next() else return null; - ?(x, y) - } - }; - - /// Zips three iterators into a single iterator that produces triples of elements. - /// The resulting iterator will stop producing elements when any of the input iterators is exhausted. - /// - /// ```motoko include=import - /// let iter1 = ["A", "B"].values(); - /// let iter2 = ["1", "2", "3"].values(); - /// let iter3 = ["x", "y", "z", "xd"].values(); - /// let zipped = Iter.zip3(iter1, iter2, iter3); - /// let result = Iter.toArray(zipped); - /// assert result == [("A", "1", "x"), ("B", "2", "y")]; // note that the unmatched elements from iter2 and iter3 are not included - /// ``` - public func zip3(self : Iter, other1 : Iter, other2 : Iter) : Iter<(A, B, C)> = object { - public func next() : ?(A, B, C) { - let ?x = self.next() else return null; - let ?y = other1.next() else return null; - let ?z = other2.next() else return null; - ?(x, y, z) - } - }; - - /// Zips two iterators into a single iterator by applying a function to zipped pairs of elements. - /// The resulting iterator will stop producing elements when either of the input iterators is exhausted. - /// - /// ```motoko include=import - /// let iter1 = ["A", "B"].values(); - /// let iter2 = ["1", "2", "3"].values(); - /// let zipped = Iter.zipWith(iter1, iter2, func (a, b) = a # b); - /// let result = Iter.toArray(zipped); - /// assert result == ["A1", "B2"]; // note that the third element from iter2 is not included, because iter1 is exhausted - /// ``` - public func zipWith(self : Iter, other : Iter, f : (A, B) -> R) : Iter = object { - public func next() : ?R { - let ?x = self.next() else return null; - let ?y = other.next() else return null; - ?f(x, y) - } - }; - - /// Zips three iterators into a single iterator by applying a function to zipped triples of elements. - /// The resulting iterator will stop producing elements when any of the input iterators is exhausted. - /// - /// ```motoko include=import - /// let iter1 = ["A", "B"].values(); - /// let iter2 = ["1", "2", "3"].values(); - /// let iter3 = ["x", "y", "z", "xd"].values(); - /// let zipped = Iter.zipWith3(iter1, iter2, iter3, func (a, b, c) = a # b # c); - /// let result = Iter.toArray(zipped); - /// assert result == ["A1x", "B2y"]; // note that the unmatched elements from iter2 and iter3 are not included - /// ``` - public func zipWith3(self : Iter, other1 : Iter, other2 : Iter, f : (A, B, C) -> R) : Iter = object { - public func next() : ?R { - let ?x = self.next() else return null; - let ?y = other1.next() else return null; - let ?z = other2.next() else return null; - ?f(x, y, z) - } - }; - - /// Checks if a predicate function is true for all elements produced by an iterator. - /// It stops consuming elements from the original iterator as soon as the predicate returns false. - /// - /// ```motoko include=import - /// assert Iter.all([1, 2, 3].values(), func (x) = x < 4); - /// assert not Iter.all([1, 2, 3].values(), func (x) = x < 3); - /// ``` - public func all(self : Iter, f : T -> Bool) : Bool { - for (x in self) { - if (not f x) return false - }; - true - }; - - /// Checks if a predicate function is true for any element produced by an iterator. - /// It stops consuming elements from the original iterator as soon as the predicate returns true. - /// - /// ```motoko include=import - /// assert Iter.any([1, 2, 3].values(), func (x) = x == 2); - /// assert not Iter.any([1, 2, 3].values(), func (x) = x == 4); - /// ``` - public func any(self : Iter, f : T -> Bool) : Bool { - for (x in self) { - if (f x) return true - }; - false - }; - - /// Finds the first element produced by an iterator for which a predicate function returns true. - /// Returns `null` if no such element is found. - /// It stops consuming elements from the original iterator as soon as the predicate returns true. - /// - /// ```motoko include=import - /// let iter = [1, 2, 3, 4].values(); - /// assert ?2 == Iter.find(iter, func (x) = x % 2 == 0); - /// ``` - public func find(self : Iter, f : T -> Bool) : ?T { - for (x in self) { - if (f x) return ?x - }; - null - }; - - /// Returns the first index in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let iter = ['A', 'B', 'C', 'D'].values(); - /// let found = Iter.findIndex(iter, func(x) { x == 'C' }); - /// assert found == ?2; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func findIndex(self : Iter, predicate : T -> Bool) : ?Nat { - for ((index, element) in enumerate(self)) { - if (predicate element) { - return ?index - } - }; - null - }; - - /// Checks if an element is produced by an iterator. - /// It stops consuming elements from the original iterator as soon as the predicate returns true. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3, 4].values(); - /// assert Iter.contains(iter, Nat.equal, 2); - /// ``` - public func contains(self : Iter, equal : (implicit : (T, T) -> Bool), value : T) : Bool { - for (x in self) { - if (equal(x, value)) return true - }; - false - }; - - /// Reduces an iterator to a single value by applying a function to each element and an accumulator. - /// The accumulator is initialized with the `initial` value. - /// It starts applying the `combine` function starting from the `initial` accumulator value and the first elements produced by the iterator. - /// - /// ```motoko include=import - /// let iter = ["A", "B", "C"].values(); - /// let result = Iter.foldLeft(iter, "S", func (acc, x) = "(" # acc # x # ")"); - /// assert result == "(((SA)B)C)"; - /// ``` - public func foldLeft(self : Iter, initial : R, combine : (R, T) -> R) : R { - var acc = initial; - for (x in self) { - acc := combine(acc, x) - }; - acc - }; - - /// Reduces an iterator to a single value by applying a function to each element in reverse order and an accumulator. - /// The accumulator is initialized with the `initial` value and it is first combined with the last element produced by the iterator. - /// It starts applying the `combine` function starting from the last elements produced by the iterator. - /// - /// **Performance note**: Since this function needs to consume the entire iterator to reverse it, - /// it has to materialize the entire iterator in memory to get to the last element to start applying the `combine` function. - /// **Use `foldLeft` or `reduce` when possible to avoid the extra memory overhead**. - /// - /// ```motoko include=import - /// let iter = ["A", "B", "C"].values(); - /// let result = Iter.foldRight(iter, "S", func (x, acc) = "(" # x # acc # ")"); - /// assert result == "(A(B(CS)))"; - /// ``` - public func foldRight(self : Iter, initial : R, combine : (T, R) -> R) : R { - foldLeft(reverse(self), initial, func(acc, x) = combine(x, acc)) - }; - - /// Reduces an iterator to a single value by applying a function to each element, starting with the first elements. - /// The accumulator is initialized with the first element produced by the iterator. - /// When the iterator is empty, it returns `null`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// assert ?6 == Iter.reduce(iter, Nat.add); - /// ``` - public func reduce(self : Iter, combine : (T, T) -> T) : ?T { - let ?first = self.next() else return null; - ?foldLeft(self, first, combine) - }; - - /// Produces an iterator containing cumulative results of applying the `combine` operator going left to right, including the `initial` value. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// let scanned = Iter.scanLeft(iter, 0, Nat.add); - /// let result = Iter.toArray(scanned); - /// assert result == [0, 1, 3, 6]; - /// ``` - public func scanLeft(self : Iter, initial : R, combine : (R, T) -> R) : Iter = object { - var acc = initial; - var isInitial = true; - public func next() : ?R { - if (isInitial) { - isInitial := false; - return ?acc - }; - switch (self.next()) { - case (?x) { - acc := combine(acc, x); - ?acc - }; - case null null - } - } - }; - - /// Produces an iterator containing cumulative results of applying the `combine` operator going right to left, including the `initial` value. - /// - /// **Performance note**: Since this function needs to consume the entire iterator to reverse it, - /// it has to materialize the entire iterator in memory to get to the last element to start applying the `combine` function. - /// **Use `scanLeft` when possible to avoid the extra memory overhead**. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// let scanned = Iter.scanRight(iter, 0, Nat.add); - /// let result = Iter.toArray(scanned); - /// assert result == [0, 3, 5, 6]; - /// ``` - public func scanRight(self : Iter, initial : R, combine : (T, R) -> R) : Iter { - scanLeft(reverse(self), initial, func(x, acc) = combine(acc, x)) - }; - - /// Creates an iterator that produces elements using the `step` function starting from the `initial` value. - /// The `step` function takes the current state and returns the next element and the next state, or `null` if the iteration is finished. - /// - /// ```motoko include=import - /// let iter = Iter.unfold(1, func (x) = if (x <= 3) ?(x, x + 1) else null); - /// let result = Iter.toArray(iter); - /// assert result == [1, 2, 3]; - /// ``` - public func unfold(initial : S, step : S -> ?(T, S)) : Iter = object { - var state = initial; - public func next() : ?T { - let ?(t, next) = step(state) else return null; - state := next; - ?t - } - }; - - // todo: unfold, iterate, cycle, range, rangeStep, rangeStepTo, rangeStepToExclusive - - /// Consumes an iterator and returns the first maximum element produced by the iterator. - /// If the iterator is empty, it returns `null`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// assert ?3 == Iter.max(iter, Nat.compare); - /// ``` - public func max(self : Iter, compare : (implicit : (T, T) -> Order.Order)) : ?T { - reduce( - self, - func(a, b) { - switch (compare(a, b)) { - case (#less) b; - case _ a - } - } - ) - }; - - /// Consumes an iterator and returns the first minimum element produced by the iterator. - /// If the iterator is empty, it returns `null`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// assert ?1 == Iter.min(iter, Nat.compare); - /// ``` - public func min(self : Iter, compare : (implicit : (T, T) -> Order.Order)) : ?T { - reduce( - self, - func(a, b) { - switch (compare(a, b)) { - case (#greater) b; - case _ a - } - } - ) - }; - - /// Creates an iterator that produces an infinite sequence of `x`. - /// ```motoko include=import - /// let iter = Iter.infinite(10); - /// assert ?10 == iter.next(); - /// assert ?10 == iter.next(); - /// assert ?10 == iter.next(); - /// // ... - /// ``` - public func infinite(item : T) : Iter = object { - public func next() : ?T { - ?item - } - }; - - /// Takes two iterators and returns a new iterator that produces - /// elements from the original iterators sequentally. - /// ```motoko include=import - /// let iter1 = [1, 2].values(); - /// let iter2 = [5, 6, 7].values(); - /// let concatenatedIter = Iter.concat(iter1, iter2); - /// let result = Iter.toArray(concatenatedIter); - /// assert result == [1, 2, 5, 6, 7]; - /// ``` - public func concat(self : Iter, other : Iter) : Iter { - var aEnded : Bool = false; - object { - public func next() : ?T { - if (aEnded) { - return other.next() - }; - switch (self.next()) { - case (?x) ?x; - case (null) { - aEnded := true; - other.next() - } - } - } - } - }; - - /// Creates an iterator that produces the elements of an Array in ascending index order. - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3]); - /// assert ?1 == iter.next(); - /// assert ?2 == iter.next(); - /// assert ?3 == iter.next(); - /// assert null == iter.next(); - /// ``` - /// @deprecated M0235 - public func fromArray(array : [T]) : Iter = array.vals(); - - /// Like `fromArray` but for Arrays with mutable elements. Captures - /// the elements of the Array at the time the iterator is created, so - /// further modifications won't be reflected in the iterator. - /// @deprecated M0235 - public func fromVarArray(array : [var T]) : Iter = array.vals(); - - /// Consumes an iterator and collects its produced elements in an Array. - /// ```motoko include=import - /// let iter = [1, 2, 3].values(); - /// assert [1, 2, 3] == Iter.toArray(iter); - /// ``` - public func toArray(self : Iter) : [T] { - // TODO: Replace implementation. This is just temporay. - type Node = { value : T; var next : ?Node }; - var first : ?Node = null; - var last : ?Node = null; - var count = 0; - - func add(value : T) { - let node : Node = { value; var next = null }; - switch (last) { - case null { - first := ?node - }; - case (?previous) { - previous.next := ?node - } - }; - last := ?node; - count += 1 - }; - - for (value in self) { - add(value) - }; - if (count == 0) { - return [] - }; - var current = first; - Prim.Array_tabulate( - count, - func(_) { - switch (current) { - case null Runtime.trap("Iter.toArray(): node must not be null"); - case (?node) { - current := node.next; - node.value - } - } - } - ) - }; - - /// Like `toArray` but for Arrays with mutable elements. - public func toVarArray(self : Iter) : [var T] { - Array.toVarArray(toArray(self)) - }; - - /// Sorted iterator. Will iterate over *all* elements to sort them, necessarily. - public func sort(self : Iter, compare : (implicit : (T, T) -> Order.Order)) : Iter { - let array = toVarArray(self); - VarArray.sortInPlace(array, compare); - fromVarArray(array) - }; - - /// Creates an iterator that produces a given item a specified number of times. - /// ```motoko include=import - /// let iter = Iter.repeat(3, 2); - /// assert ?3 == iter.next(); - /// assert ?3 == iter.next(); - /// assert null == iter.next(); - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func repeat(item : T, count : Nat) : Iter = object { - var remaining = count; - public func next() : ?T { - if (remaining == 0) { - null - } else { - remaining -= 1; - ?item - } - } - }; - - /// Creates a new iterator that produces elements from the original iterator in reverse order. - /// Note: This function needs to consume the entire iterator to reverse it. - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3]); - /// let reversed = Iter.reverse(iter); - /// assert ?3 == reversed.next(); - /// assert ?2 == reversed.next(); - /// assert ?1 == reversed.next(); - /// assert null == reversed.next(); - /// ``` - /// - /// Runtime: O(n) where n is the number of elements in the iterator - /// - /// Space: O(n) where n is the number of elements in the iterator - public func reverse(self : Iter) : Iter { - var acc : Types.Pure.List = null; - for (x in self) { - acc := ?(x, acc) - }; - object { - public func next() : ?T { - switch acc { - case null null; - case (?(h, t)) { - acc := t; - ?h - } - } - } - } - }; - -} diff --git a/.mops/core@2.4.0/src/List.mo b/.mops/core@2.4.0/src/List.mo deleted file mode 100644 index 07f98eb..0000000 --- a/.mops/core@2.4.0/src/List.mo +++ /dev/null @@ -1,3138 +0,0 @@ -/// A mutable growable array data structure with efficient random access and dynamic resizing. -/// `List` provides O(1) access time and O(sqrt(n)) memory overhead. In contrast, `pure/List` is a purely functional linked list. -/// Can be declared `stable` for orthogonal persistence. -/// -/// This implementation is adapted with permission from the `vector` Mops package created by Research AG. -/// -/// Copyright: 2023 MR Research AG -/// Main author: Andrii Stepanov (AStepanov25) -/// Contributors: Timo Hanke (timohanke), Andy Gura (andygura), react0r-com -/// -/// ```motoko name=import -/// import List "mo:core/List"; -/// ``` - -import PureList "pure/List"; -import Prim "mo:⛔"; -import Nat32 "Nat32"; -import Array "Array"; -import Nat "Nat"; -import Option "Option"; -import VarArray "VarArray"; -import Types "Types"; - -module { - /// `List` provides a mutable list of elements of type `T`. - /// Based on the paper "Resizable Arrays in Optimal Time and Space" by Brodnik, Carlsson, Demaine, Munro and Sedgewick (1999). - /// Since this is internally a two-dimensional array the access times for put and get operations - /// will naturally be 2x slower than Buffer and Array. However, Array is not resizable and Buffer - /// has `O(size)` memory waste. - /// - /// The maximum number of elements in a `List` is 2^32. - public type List = Types.List; - - let INTERNAL_ERROR = "List: internal error"; - - /// Creates a new empty List for elements of type T. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); // Creates a new List - /// ``` - public func empty() : List = { - // the first block is always empty and is present in each List - // this is done to optimize locate, at, get, etc - var blocks = [var [var]]; - // can't be 0 in any List - var blockIndex = 1; - var elementIndex = 0 - }; - - /// Returns a new list with capacity and size 1, containing `element`. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.singleton(1); - /// assert List.toText(list, Nat.toText) == "List[1]"; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func singleton(element : T) : List = { - var blockIndex = 2; - var blocks = [var [var], [var ?element]]; - var elementIndex = 0 - }; - - func repeatInternal(initValue : ?T, size : Nat) : List { - let (blockIndex, elementIndex) = locate(size); - - let blocks = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) { blockIndex - 1 } else blockIndex)); - let dataBlocks = VarArray.repeat<[var ?T]>([var], blocks); - var i = 1; - while (i < blockIndex) { - dataBlocks[i] := VarArray.repeat(initValue, dataBlockSize(i)); - i += 1 - }; - if (elementIndex != 0) { - dataBlocks[blockIndex] := if (Option.isNull(initValue)) VarArray.repeat( - null, - dataBlockSize(blockIndex) - ) else VarArray.tabulate( - dataBlockSize(blockIndex), - func i = if (i < elementIndex) initValue else null - ) - }; - - { - var blocks = dataBlocks; - var blockIndex = blockIndex; - var elementIndex = elementIndex - } - }; - - /// Creates a new List with `size` copies of the initial value. - /// - /// Example: - /// ```motoko include=import - /// let list = List.repeat(2, 4); - /// assert List.toArray(list) == [2, 2, 2, 2]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func repeat(initValue : T, size : Nat) : List = repeatInternal(?initValue, size); - - /// Fills all elements in the list with the given value. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3]); - /// List.fill(list, 0); // fills the list with 0 - /// assert List.toArray(list) == [0, 0, 0]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func fill(self : List, value : T) { - let blocks = self.blocks; - let blockCount = blocks.size(); - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = if (i == blockIndex) elementIndex else db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - db[j] := ?value; - j += 1 - }; - i += 1 - } - }; - - /// Converts a mutable `List` to a purely functional `PureList`. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3]); - /// let pureList = List.toPure(list); // converts to immutable PureList - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// @deprecated M0235 - public func toPure(self : List) : PureList.List { - var result : PureList.List = null; - - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) result := ?(x, result); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - }; - - result - }; - - /// Converts a purely functional `PureList` to a `List`. - /// - /// Example: - /// ```motoko include=import - /// import PureList "mo:core/pure/List"; - /// - /// let pureList = PureList.fromArray([1, 2, 3]); - /// let list = List.fromPure(pureList); // converts to List - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// @deprecated M0235 - public func fromPure(pure : PureList.List) : List { - var p = pure; - var list = empty(); - loop { - switch (p) { - case (?(x, xs)) { - add(list, x); - p := xs - }; - case null return list - } - } - }; - - func addRepeatInternal(list : List, initValue : ?T, count : Nat) { - let (b, e) = locate(size(list) + count); - let blocksCount = newIndexBlockLength(Nat32.fromNat(if (e == 0) b - 1 else b)); - - let oldBlocksCount = list.blocks.size(); - if (oldBlocksCount < blocksCount) { - let oldBlocks = list.blocks; - let blocks = VarArray.repeat<[var ?T]>([var], blocksCount); - var i = 0; - while (i < oldBlocksCount) { - blocks[i] := oldBlocks[i]; - i += 1 - }; - list.blocks := blocks - }; - - let blocks = list.blocks; - var blockIndex = list.blockIndex; - var elementIndex = list.elementIndex; - - var cnt = count; - label L while (cnt > 0) { - if (blocks[blockIndex].size() == 0) { - let dbSize = dataBlockSize(blockIndex); - if (cnt >= dbSize) { - blocks[blockIndex] := VarArray.repeat(initValue, dbSize); - blockIndex += 1; - cnt -= dbSize; - continue L - }; - blocks[blockIndex] := VarArray.repeat(null, dbSize) - }; - - let block = blocks[blockIndex]; - let dbSize = block.size(); - let to = Nat.min(elementIndex + cnt, dbSize); - cnt -= to - elementIndex; - - while (elementIndex < to) { - block[elementIndex] := initValue; - elementIndex += 1 - }; - - if (elementIndex == dbSize) { - elementIndex := 0; - blockIndex += 1 - } - }; - - list.blockIndex := blockIndex; - list.elementIndex := elementIndex - }; - - private func reserve(list : List, size : Nat) { - let blockIndex = list.blockIndex; - let elementIndex = list.elementIndex; - - addRepeatInternal(list, null, size); - - list.blockIndex := blockIndex; - list.elementIndex := elementIndex - }; - - /// Add to list `count` copies of the initial value. - /// - /// ```motoko include=import - /// let list = List.repeat(2, 4); // [2, 2, 2, 2] - /// List.addRepeat(list, 2, 1); // [2, 2, 2, 2, 1, 1] - /// ``` - /// - /// The maximum number of elements in a `List` is 2^32. - /// - /// Runtime: `O(count)` - public func addRepeat(self : List, initValue : T, count : Nat) = addRepeatInternal(self, ?initValue, count); - - /// Truncates the list to the specified size. - /// If the new size is larger than the current size, it will do nothing. - /// If the new size is equal to the current list size, after the operation list will be equal to cloned version of itself. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4, 5]); - /// List.truncate(list, 3); // list is now [1, 2, 3] - /// assert List.toArray(list) == [1, 2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func truncate(self : List, newSize : Nat) { - if (newSize > size(self)) return; - - // if newSize == size(self) then after the operation self will be equal to List.clone(self) - let (blockIndex, elementIndex) = locate(newSize); - self.blockIndex := blockIndex; - self.elementIndex := elementIndex; - let newBlocksCount = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) blockIndex - 1 else blockIndex)); - - let newBlocks = if (newBlocksCount < self.blocks.size()) { - let oldDataBlocks = self.blocks; - self.blocks := VarArray.tabulate<[var ?T]>(newBlocksCount, func(i) = oldDataBlocks[i]); - self.blocks - } else self.blocks; - - var i = if (elementIndex == 0) blockIndex else blockIndex + 1; - while (i < newBlocksCount) { - newBlocks[i] := [var]; - i += 1 - }; - if (elementIndex != 0) { - let block = newBlocks[blockIndex]; - var i = elementIndex; - var to = block.size(); - while (i < to) { - block[i] := null; - i += 1 - } - } - }; - - /// Resets the list to size 0, de-referencing all elements. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// List.clear(list); // list is now empty - /// assert List.toArray(list) == []; - /// ``` - /// - /// Runtime: `O(1)` - public func clear(self : List) { - self.blocks := [var [var]]; - self.blockIndex := 1; - self.elementIndex := 0 - }; - - /// Creates a list of size `size`. Each element at index i - /// is created by applying `generator` to i. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.tabulate(4, func i = i * 2); - /// assert List.toArray(list) == [0, 2, 4, 6]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `generator` runs in O(1) time and space. - public func tabulate(size : Nat, generator : Nat -> T) : List { - let (blockIndex, elementIndex) = locate(size); - - let blocks = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) { blockIndex - 1 } else blockIndex)); - let dataBlocks = VarArray.repeat<[var ?T]>([var], blocks); - - var i = 1; - var pos = 0; - - while (i < blockIndex) { - let len = dataBlockSize(i); - dataBlocks[i] := VarArray.tabulate(len, func i = ?generator(pos + i)); - pos += len; - i += 1 - }; - if (elementIndex != 0 and blockIndex < blocks) { - dataBlocks[i] := VarArray.tabulate( - dataBlockSize(blockIndex), - func i = if (i < elementIndex) ?generator(pos + i) else null - ) - }; - - { - var blocks = dataBlocks; - var blockIndex = blockIndex; - var elementIndex = elementIndex - } - }; - - /// Combines a list of lists into a single list. Retains the original - /// ordering of the elements. - /// - /// This has better performance compared to `List.join()`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let lists = List.fromArray>([ - /// List.fromArray([0, 1, 2]), List.fromArray([2, 3]), List.fromArray([]), List.fromArray([4]) - /// ]); - /// let flatList = List.flatten(lists); - /// assert List.equal(flatList, List.fromArray([0, 1, 2, 2, 3, 4]), Nat.equal); - /// ``` - /// - /// Runtime: O(number of elements in list) - /// - /// Space: O(number of elements in list) - public func flatten(self : List>) : List { - var sz = 0; - forEach>(self, func(sublist) = sz += size(sublist)); - - let result = repeatInternal(null, sz); - result.blockIndex := 1; - result.elementIndex := 0; - - forEach>( - self, - func(sublist) { - forEach( - sublist, - func(item) { - add(result, item) - } - ) - } - ); - result - }; - - /// Combines an iterator of lists into a single list. - /// Retains the original ordering of the elements. - /// - /// Consider using `List.flatten()` for better performance. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let lists = [List.fromArray([0, 1, 2]), List.fromArray([2, 3]), List.fromArray([]), List.fromArray([4])]; - /// let joinedList = List.join(lists.vals()); - /// assert List.equal(joinedList, List.fromArray([0, 1, 2, 2, 3, 4]), Nat.equal); - /// ``` - /// - /// Runtime: O(number of elements in list) - /// - /// Space: O(number of elements in list) - public func join(self : Types.Iter>) : List { - var result = empty(); - for (list in self) { - reserve(result, size(list)); - forEach(list, func item = addUnsafe(result, item)) - }; - result - }; - - /// Returns a copy of a List, with the same size. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 1); - /// - /// let clone = List.clone(list); - /// assert List.toArray(clone) == [1]; - /// ``` - /// - /// Runtime: `O(size)` - public func clone(self : List) : List = { - var blocks = VarArray.tabulate<[var ?T]>( - Nat.min( - newIndexBlockLength(Nat32.fromNat(if (self.elementIndex == 0) self.blockIndex - 1 else self.blockIndex)), - self.blocks.size() - ), - func(i) = VarArray.clone(self.blocks[i]) - ); - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - }; - - /// Creates a new list by applying the provided function to each element in the input list. - /// The resulting list has the same size as the input list. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.singleton(123); - /// let textList = List.map(list, Nat.toText); - /// assert List.toArray(textList) == ["123"]; - /// ``` - /// - /// Runtime: `O(size)` - public func map(self : List, f : T -> R) : List { - let blocksCount = Nat.min( - newIndexBlockLength(Nat32.fromNat(if (self.elementIndex == 0) self.blockIndex - 1 else self.blockIndex)), - self.blocks.size() - ); - let blocks = VarArray.repeat<[var ?R]>([var], blocksCount); - - var i = 1; - label l while (i < blocksCount) { - let oldBlock = self.blocks[i]; - let blockSize = oldBlock.size(); - let newBlock = VarArray.repeat(null, blockSize); - blocks[i] := newBlock; - var j = 0; - - while (j < blockSize) { - switch (oldBlock[j]) { - case (?item) newBlock[j] := ?f(item); - case null break l - }; - j += 1 - }; - i += 1 - }; - - { - var blocks = blocks; - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - } - }; - - /// Applies `f` to each element of `list` in place, - /// retaining the original ordering of elements. - /// This modifies the original list. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([0, 1, 2, 3]); - /// List.mapInPlace(list, func x = x * 3); - /// assert List.equal(list, List.fromArray([0, 3, 6, 9]), Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapInPlace(self : List, f : T -> T) { - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) db[j] := ?f(x); - case null return - }; - j += 1 - }; - i += 1 - } - }; - - /// Creates a new list by applying `f` to each element in `list` and its index. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([10, 10, 10, 10]); - /// let newList = List.mapEntries(list, func (x, i) = i * x); - /// assert List.equal(newList, List.fromArray([0, 10, 20, 30]), Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapEntries(self : List, f : (T, Nat) -> R) : List { - let blocks = VarArray.repeat<[var ?R]>([var], self.blocks.size()); - let blocksCount = self.blocks.size(); - - var index = 0; - - var i = 1; - label l while (i < blocksCount) { - let oldBlock = self.blocks[i]; - let blockSize = oldBlock.size(); - let newBlock = VarArray.repeat(null, blockSize); - blocks[i] := newBlock; - var j = 0; - - while (j < blockSize) { - switch (oldBlock[j]) { - case (?item) newBlock[j] := ?f(item, index); - case null break l - }; - j += 1; - index += 1 - }; - i += 1 - }; - - { - var blocks = blocks; - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - } - }; - - /// Creates a new list by applying `f` to each element in `list`. - /// If any invocation of `f` produces an `#err`, returns an `#err`. Otherwise - /// returns an `#ok` containing the new list. - /// - /// ```motoko include=import - /// import Result "mo:core/Result"; - /// - /// let list = List.fromArray([4, 3, 2, 1, 0]); - /// // divide 100 by every element in the list - /// let result = List.mapResult(list, func x { - /// if (x > 0) { - /// #ok(100 / x) - /// } else { - /// #err "Cannot divide by zero" - /// } - /// }); - /// assert Result.isErr(result); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapResult(self : List, f : T -> Types.Result) : Types.Result, E> { - var error : ?E = null; - - let blocks = VarArray.repeat<[var ?R]>([var], self.blocks.size()); - let blocksCount = self.blocks.size(); - - var i = 1; - while (i < blocksCount) { - let oldBlock = self.blocks[i]; - let blockSize = oldBlock.size(); - let newBlock = VarArray.repeat(null, blockSize); - blocks[i] := newBlock; - var j = 0; - - while (j < blockSize) { - switch (oldBlock[j]) { - case (?item) newBlock[j] := switch (f(item)) { - case (#ok x) ?x; - case (#err e) switch (error) { - case (null) { - error := ?e; - null - }; - case (?_) null - } - }; - case null return switch (error) { - case (null) return #ok { - var blocks = blocks; - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - }; - case (?e) return #err e - } - }; - j += 1 - }; - i += 1 - }; - - switch (error) { - case (null) return #ok { - var blocks = blocks; - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - }; - case (?e) return #err e - } - }; - - /// Returns a new list containing only the elements from `list` for which the predicate returns true. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4]); - /// let evenNumbers = List.filter(list, func x = x % 2 == 0); - /// assert List.toArray(evenNumbers) == [2, 4]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func filter(self : List, predicate : T -> Bool) : List { - let filtered = empty(); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return filtered; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (predicate(x)) add(filtered, x); - case null return filtered - }; - j += 1 - }; - i += 1 - }; - - filtered - }; - - /// Retains only the elements in `list` for which the predicate returns true. - /// Modifies the original list in place. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4]); - /// List.retain(list, func x = x % 2 == 0); - /// assert List.toArray(list) == [2, 4]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(sqrt(size))` if `list` was truncated otherwise `O(1)` - public func retain(self : List, predicate : T -> Bool) { - self.blockIndex := 1; - self.elementIndex := 0; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - label l while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) break l; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (predicate(x)) addUnsafe(self, x); - case null break l - }; - j += 1 - }; - i += 1 - }; - - truncate(self, size(self)) - }; - - /// Returns a new list containing all elements from `list` for which the function returns ?element. - /// Discards all elements for which the function returns null. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4]); - /// let doubled = List.filterMap(list, func x = if (x % 2 == 0) ?(x * 2) else null); - /// assert List.toArray(doubled) == [4, 8]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func filterMap(self : List, f : T -> ?R) : List { - let filtered = empty(); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return filtered; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) switch (f(x)) { - case (?y) add(filtered, y); - case null {} - }; - case null return filtered - }; - j += 1 - }; - i += 1 - }; - - filtered - }; - - /// Creates a new list by applying `k` to each element in `list`, - /// and concatenating the resulting iterators in order. - /// - /// ```motoko include=import - /// import Int "mo:core/Int" - /// - /// let list = List.fromArray([1, 2, 3, 4]); - /// let newList = List.flatMap(list, func x = [x, -x].vals()); - /// assert List.equal(newList, List.fromArray([1, -1, 2, -2, 3, -3, 4, -4]), Int.equal); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `k` runs in O(1) time and space. - public func flatMap(self : List, k : T -> Types.Iter) : List { - let result = empty(); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return result; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) for (y in k(x)) add(result, y); - case _ return result - }; - j += 1 - }; - i += 1 - }; - - result - }; - - func indexByBlockElement(blockIndex : Nat, elementIndex : Nat) : Nat { - let d = Nat32.fromNat(blockIndex); - - // We call all data blocks of the same capacity an "epoch". We number the epochs 0,1,2,... - // A data block is in epoch e iff the data block has capacity 2 ** e. - // Each epoch starting with epoch 1 spans exactly two super blocks. - // Super block s falls in epoch ceil(s/2). - - // epoch of last data block - // e = 32 - lz - let lz = Nat32.bitcountLeadingZero(d / 3); - - // capacity of all prior epochs combined - // capacity_before_e = 2 * 4 ** (e - 1) - 1 - - // data blocks in all prior epochs combined - // blocks_before_e = 3 * 2 ** (e - 1) - 2 - - // then size = d * 2 ** e + i - c - // where c = blocks_before_e * 2 ** e - capacity_before_e - - // there can be overflows, but the result is without overflows, so use addWrap and subWrap - // we don't erase bits by >>, so to use <>> is ok - Nat32.toNat((d -% (1 <>> lz)) <>> lz +% Nat32.fromNat(elementIndex)) - }; - - /// Returns the current number of elements in the list. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// assert List.size(list) == 0 - /// ``` - /// - /// Runtime: `O(1)` (with some internal calculations) - public func size(self : List) : Nat { - // due to the design of List (blockIndex, elementIndex) pair points - // exactly to the place where size-th element should be added - // so, it's the inlined version of indexByBlockElement - let d = Nat32.fromNat(self.blockIndex); - let lz = Nat32.bitcountLeadingZero(d / 3); - Nat32.toNat((d -% (1 <>> lz)) <>> lz +% Nat32.fromNat(self.elementIndex)) - }; - - func dataBlockSize(blockIndex : Nat) : Nat { - // formula for the size of given blockIndex - // don't call it for blockIndex == 0 - Nat32.toNat(1 <>> Nat32.bitcountLeadingZero(Nat32.fromNat(blockIndex) / 3)) - }; - - func newIndexBlockLength(blockIndex : Nat32) : Nat { - if (blockIndex <= 1) 2 else { - let s = 30 - Nat32.bitcountLeadingZero(blockIndex); - Nat32.toNat(((blockIndex >> s) +% 1) << s) - } - }; - - func growIndexBlockIfNeeded(list : List) { - if (list.blocks.size() == list.blockIndex) { - let newBlocks = VarArray.repeat<[var ?T]>([var], newIndexBlockLength(Nat32.fromNat(list.blockIndex))); - var i = 0; - while (i < list.blockIndex) { - newBlocks[i] := list.blocks[i]; - i += 1 - }; - list.blocks := newBlocks - } - }; - - func shrinkIndexBlockIfNeeded(list : List) { - let blockIndex = Nat32.fromNat(list.blockIndex); - // kind of index of the first block in the super block - if ((blockIndex << Nat32.bitcountLeadingZero(blockIndex)) << 2 == 0) { - let newLength = newIndexBlockLength(blockIndex); - if (newLength < list.blocks.size()) { - let newBlocks = VarArray.repeat<[var ?T]>([var], newLength); - var i = 0; - while (i < newLength) { - newBlocks[i] := list.blocks[i]; - i += 1 - }; - list.blocks := newBlocks - } - } - }; - - /// Adds a single element to the end of a List, - /// allocating a new internal data block if needed, - /// and resizing the internal index block if needed. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 0); // add 0 to list - /// List.add(list, 1); - /// List.add(list, 2); - /// List.add(list, 3); - /// assert List.toArray(list) == [0, 1, 2, 3]; - /// ``` - /// - /// The maximum number of elements in a `List` is 2^32. - /// - /// Amortized Runtime: `O(1)`, Worst Case Runtime: `O(sqrt(n))` - public func add(self : List, element : T) { - var elementIndex = self.elementIndex; - if (elementIndex == 0) { - growIndexBlockIfNeeded(self); - let blockIndex = self.blockIndex; - - // When removing last we keep one more data block, so can be not empty - if (self.blocks[blockIndex].size() == 0) { - self.blocks[blockIndex] := VarArray.repeat( - null, - dataBlockSize(blockIndex) - ) - } - }; - - let lastDataBlock = self.blocks[self.blockIndex]; - - lastDataBlock[elementIndex] := ?element; - - elementIndex += 1; - if (elementIndex == lastDataBlock.size()) { - elementIndex := 0; - self.blockIndex += 1 - }; - self.elementIndex := elementIndex - }; - - // Add an element without checking and resizing the List - private func addUnsafe(list : List, element : T) { - var elementIndex = list.elementIndex; - let lastDataBlock = list.blocks[list.blockIndex]; - lastDataBlock[elementIndex] := ?element; - - elementIndex += 1; - if (elementIndex == lastDataBlock.size()) { - elementIndex := 0; - list.blockIndex += 1 - }; - list.elementIndex := elementIndex - }; - - /// Removes and returns the last item in the list or `null` if - /// the list is empty. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// assert List.removeLast(list) == ?11; - /// assert List.removeLast(list) == ?10; - /// assert List.removeLast(list) == null; - /// ``` - /// - /// Amortized Runtime: `O(1)`, Worst Case Runtime: `O(sqrt(n))` - /// - /// Amortized Space: `O(1)`, Worst Case Space: `O(sqrt(n))` - public func removeLast(self : List) : ?T { - var elementIndex = self.elementIndex; - if (elementIndex == 0) { - var blockIndex = self.blockIndex; - if (blockIndex == 1) { - return null - }; - - shrinkIndexBlockIfNeeded(self); - - blockIndex -= 1; - elementIndex := self.blocks[blockIndex].size(); - - // Keep one totally empty block when removing - if (blockIndex + 2 < self.blocks.size()) self.blocks[blockIndex + 2] := [var]; - - self.blockIndex := blockIndex - }; - elementIndex -= 1; - - let lastDataBlock = self.blocks[self.blockIndex]; - - let element = lastDataBlock[elementIndex]; - lastDataBlock[elementIndex] := null; - - self.elementIndex := elementIndex; - return element - }; - - func locate(index : Nat) : (Nat, Nat) { - // see comments in tests - let i = Nat32.fromNat(index); - let lz = Nat32.bitcountLeadingZero(i); - let lz2 = lz >> 1; - if (lz & 1 == 0) { - (Nat32.toNat(((i << lz2) >> 16) ^ (0x10000 >> lz2)), Nat32.toNat(i & (0xFFFF >> lz2))) - } else { - (Nat32.toNat(((i << lz2) >> 15) ^ (0x18000 >> lz2)), Nat32.toNat(i & (0x7FFF >> lz2))) - } - }; - - /// Returns the element at index `index`. Indexing is zero-based. - /// Traps if `index >= size`, error message may not be descriptive. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// assert List.at(list, 0) == 10; - /// ``` - /// - /// Runtime: `O(1)` - public func at(self : List, index : Nat) : T { - // inlined version of: - // let (a,b) = locate(index); - // switch(self.blocks[a][b]) { - // case (?element) element; - // case (null) Prim.trap ""; - // }; - let i = Nat32.fromNat(index); - let lz = Nat32.bitcountLeadingZero(i); - let lz2 = lz >> 1; - switch ( - if (lz & 1 == 0) { - self.blocks[Nat32.toNat(((i << lz2) >> 16) ^ (0x10000 >> lz2))][Nat32.toNat(i & (0xFFFF >> lz2))] - } else { - self.blocks[Nat32.toNat(((i << lz2) >> 15) ^ (0x18000 >> lz2))][Nat32.toNat(i & (0x7FFF >> lz2))] - } - ) { - case (?result) return result; - case (_) Prim.trap "List index out of bounds in get" - } - }; - - /// Returns the element at index `index` as an option. - /// Returns `null` when `index >= size`. Indexing is zero-based. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// assert List.get(list, 0) == ?10; - /// assert List.get(list, 2) == null; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - /// @deprecated M0235 - public func get(self : List, index : Nat) : ?T { - // inlined version of locate - let (a, b) = do { - let i = Nat32.fromNat(index); - let lz = Nat32.bitcountLeadingZero(i); - let lz2 = lz >> 1; - if (lz & 1 == 0) { - (Nat32.toNat(((i << lz2) >> 16) ^ (0x10000 >> lz2)), Nat32.toNat(i & (0xFFFF >> lz2))) - } else { - (Nat32.toNat(((i << lz2) >> 15) ^ (0x18000 >> lz2)), Nat32.toNat(i & (0x7FFF >> lz2))) - } - }; - if (a < self.blockIndex or self.elementIndex != 0 and a == self.blockIndex) { - self.blocks[a][b] - } else null - }; - - /// Overwrites the current element at `index` with `element`. - /// Traps if `index` >= size, error message may not be descriptive. Indexing is zero-based. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.put(list, 0, 20); // overwrites 10 at index 0 with 20 - /// assert List.toArray(list) == [20]; - /// ``` - /// - /// Runtime: `O(1)` - public func put(self : List, index : Nat, value : T) { - let i = Nat32.fromNat(index); - let lz = Nat32.bitcountLeadingZero(i); - let lz2 = lz >> 1; - let (block, element) = if (lz & 1 == 0) { - (self.blocks[Nat32.toNat(((i << lz2) >> 16) ^ (0x10000 >> lz2))], Nat32.toNat(i & (0xFFFF >> lz2))) - } else { - (self.blocks[Nat32.toNat(((i << lz2) >> 15) ^ (0x18000 >> lz2))], Nat32.toNat(i & (0x7FFF >> lz2))) - }; - - switch (block[element]) { - case (?_) block[element] := ?value; - case _ Prim.trap "List index out of bounds in put" - } - }; - - /// Sorts the elements in the list according to `compare`. - /// Sort is deterministic, stable, and in-place. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 3); - /// List.add(list, 1); - /// List.add(list, 2); - /// List.sortInPlace(list, Nat.compare); - /// assert List.toArray(list) == [1, 2, 3]; - /// ``` - /// - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sortInPlace(self : List, compare : (implicit : (T, T) -> Types.Order)) { - if (size(self) < 2) return; - let array = toVarArray(self); - - VarArray.sortInPlace(array, compare); - - var index = 0; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?_) db[j] := ?array[index]; - case _ return - }; - index += 1; - j += 1 - }; - i += 1 - } - }; - - /// Sorts the elements in the list according to `compare`. - /// Sort is deterministic, stable, and in-place. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 3); - /// List.add(list, 1); - /// List.add(list, 2); - /// let sorted = List.sort(list, Nat.compare); - /// assert List.toArray(sorted) == [1, 2, 3]; - /// ``` - /// - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sort(self : List, compare : (implicit : (T, T) -> Types.Order)) : List { - let array = toVarArray(self); - VarArray.sortInPlace(array, compare); - fromVarArray(array) - }; - - /// Checks whether the `list` is sorted. - /// - /// Example: - /// ``` - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// assert List.isSorted(list, Nat.compare); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func isSorted(self : List, compare : (implicit : (T, T) -> Types.Order)) : Bool { - var prev = switch (first(self)) { - case (?x) x; - case _ return true - }; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return true; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) switch (compare(x, prev)) { - case (#greater or #equal) prev := x; - case (#less) return false - }; - case null return true - }; - j += 1 - }; - i += 1 - }; - - true - }; - - /// Remove adjacent duplicates from the `list`, if the `list` is sorted all elements will be unique. - /// - /// Example: - /// ``` - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 1, 2, 2, 3]); - /// List.deduplicate(list, Nat.equal); - /// assert List.equal(list, List.fromArray([1, 2, 3]), Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func deduplicate(self : List, equal : (implicit : (T, T) -> Bool)) { - var prev = switch (first(self)) { - case (?x) x; - case _ return - }; - - self.blockIndex := 1; - self.elementIndex := 0; - - addUnsafe(self, prev); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - label l while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return break l; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) { - if (not equal(x, prev)) addUnsafe(self, x); - prev := x - }; - case null break l - }; - j += 1 - }; - i += 1 - }; - - truncate(self, size(self)) - }; - - /// Finds the first index of `element` in `list` using equality of elements defined - /// by `equal`. Returns `null` if `element` is not found. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.indexOf(list, Nat.equal, 3) == ?2; - /// assert List.indexOf(list, Nat.equal, 5) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// *Runtime and space assumes that `equal` runs in `O(1)` time and space. - public func indexOf(self : List, equal : (implicit : (T, T) -> Bool), element : T) : ?Nat { - if (isEmpty(self)) return null; - nextIndexOf(self, equal, element, 0) - }; - - /// Returns the index of the next occurence of `element` in the `list` starting from the `from` index (inclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let list = List.fromArray(['c', 'o', 'f', 'f', 'e', 'e']); - /// assert List.nextIndexOf(list, Char.equal, 'c', 0) == ?0; - /// assert List.nextIndexOf(list, Char.equal, 'f', 0) == ?2; - /// assert List.nextIndexOf(list, Char.equal, 'f', 2) == ?2; - /// assert List.nextIndexOf(list, Char.equal, 'f', 3) == ?3; - /// assert List.nextIndexOf(list, Char.equal, 'f', 4) == null; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func nextIndexOf(self : List, equal : (implicit : (T, T) -> Bool), element : T, fromInclusive : Nat) : ?Nat { - if (fromInclusive >= size(self)) Prim.trap "List index out of bounds in nextIndexOf"; - - let (blockIndex, elementIndex) = locate(fromInclusive); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = blockIndex; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return null; - - var j = if (i == blockIndex) elementIndex else 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (equal(x, element)) return ?indexByBlockElement(i, j); - case null return null - }; - j += 1 - }; - i += 1 - }; - null - }; - - /// Finds the last index of `element` in `list` using equality of elements defined - /// by `equal`. Returns `null` if `element` is not found. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 2, 3, 4, 2, 2]); - /// - /// assert List.lastIndexOf(list, Nat.equal, 2) == ?5; - /// assert List.lastIndexOf(list, Nat.equal, 5) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// *Runtime and space assumes that `equal` runs in `O(1)` time and space. - public func lastIndexOf(self : List, equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = prevIndexOf( - self, - equal, - element, - size(self) - ); - - /// Returns the index of the previous occurence of `element` in the `list` starting from the `from` index (exclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let list = List.fromArray(['c', 'o', 'f', 'f', 'e', 'e']); - /// assert List.prevIndexOf(list, Char.equal, 'c', List.size(list)) == ?0; - /// assert List.prevIndexOf(list, Char.equal, 'e', List.size(list)) == ?5; - /// assert List.prevIndexOf(list, Char.equal, 'e', 5) == ?4; - /// assert List.prevIndexOf(list, Char.equal, 'e', 4) == null; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func prevIndexOf(self : List, equal : (implicit : (T, T) -> Bool), element : T, fromExclusive : Nat) : ?Nat { - if (fromExclusive > size(self)) Prim.trap "List index out of bounds in prevIndexOf"; - - let blocks = self.blocks; - let (blockIndex, elementIndex) = locate(fromExclusive); - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) if (equal(x, element)) return ?indexByBlockElement(i, j); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - }; - - null - }; - - /// Returns the first value in `list` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let list = List.fromArray([1, 9, 4, 8]); - /// let found = List.find(list, func(x) { x > 8 }); - /// assert found == ?9; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func find(self : List, predicate : T -> Bool) : ?T { - Option.map(findIndex(self, predicate), func(i) = at(self, i)) - }; - - /// Finds the index of the first element in `list` for which `predicate` is true. - /// Returns `null` if no such element is found. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.findIndex(list, func(i) { i % 2 == 0 }) == ?1; - /// assert List.findIndex(list, func(i) { i > 5 }) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func findIndex(self : List, predicate : T -> Bool) : ?Nat { - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return null; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (predicate(x)) return ?indexByBlockElement(i, j); - case null return null - }; - j += 1 - }; - i += 1 - }; - null - }; - - /// Finds the index of the last element in `list` for which `predicate` is true. - /// Returns `null` if no such element is found. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.findLastIndex(list, func(i) { i % 2 == 0 }) == ?3; - /// assert List.findLastIndex(list, func(i) { i > 5 }) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func findLastIndex(self : List, predicate : T -> Bool) : ?Nat { - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) if (predicate(x)) return ?indexByBlockElement(i, j); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - }; - - null - }; - - /// Performs binary search on a sorted list to find the index of the `element`. - /// Returns `#found(index)` if the element is found, or `#insertionIndex(index)` with the index - /// where the element would be inserted according to the ordering if not found. - /// - /// If there are multiple equal elements, no guarantee is made about which index is returned. - /// The list must be sorted in ascending order according to the `compare` function. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 3, 5, 7, 9, 11]); - /// assert List.binarySearch(list, Nat.compare, 5) == #found(2); - /// assert List.binarySearch(list, Nat.compare, 6) == #insertionIndex(3); - /// ``` - /// - /// Runtime: `O(log(size))` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `compare` runs in `O(1)` time and space. - public func binarySearch(self : List, compare : (implicit : (T, T) -> Types.Order), element : T) : { - #found : Nat; - #insertionIndex : Nat - } { - // We call all data blocks of the same capacity an "epoch". We number the epochs 0,1,2,... - // A data block is in epoch e iff the data block has capacity 2 ** e. - // Each epoch starting with epoch 1 spans exactly two super blocks. - // Super block s falls in epoch ceil(s/2). - // Each epoch except e=0 contains 3 * 2 ** (e - 1) data blocks - - let blocks = self.blocks; - let b = self.blockIndex - (if (self.elementIndex == 0) 1 else 0) : Nat; - - // block index x such that blocks[x][0] <= element - let lessOrEqual = do { - // epoch of the last data block - let epoch = 32 - Nat32.bitcountLeadingZero(Nat32.fromNat(b) / 3); - // initially block index is the first in the epoch - var lessOrEqual = Nat32.toNat((1 << epoch) / 2); - - // lessOrEqual * 3 is always the first data block in an epoch - // while the first element of the first data block in an epoch is actually grater then element go to the previous epoch - // as the last epoch is half of the array we each iteration of the search divides the interval in four - while (lessOrEqual != 0 and compare(Option.unwrap(blocks[lessOrEqual * 3][0]), element) == #greater) { - lessOrEqual /= 2 - }; - - lessOrEqual * 3 - }; - - // Linear search in e=0, there are just two elements - if (lessOrEqual == 0) { - let to = Nat.min(size(self), 2); - for (i in Nat.range(0, to)) { - let x = at(self, i); - switch (compare(x, element)) { - case (#less) {}; - case (#equal) return #found(i); - case (#greater) return #insertionIndex(i) - } - }; - return #insertionIndex(to) - }; - - // binary search the blockIndex in [left, right) - let blockIndex = do { - // guarateed less or equal to element - var left = lessOrEqual; - // right is either outside of the array or greater than element - var right = Nat.min(b + 1, lessOrEqual * 2); - while (right - left : Nat > 1) { - let mid = (left + right) / 2; - switch (compare(Option.unwrap(blocks[mid][0]), element)) { - case (#less) left := mid; - case (#greater) right := mid; - case (#equal) return #found(indexByBlockElement(mid, 0)) - } - }; - left - }; - - // binary search the elementIndex - let elementIndex = do { - let block = blocks[blockIndex]; - var left = 0; - var right = if (blockIndex == self.blockIndex) self.elementIndex else block.size(); - while (left != right) { - let mid = (left + right) / 2; - switch (compare(Option.unwrap(block[mid]), element)) { - case (#less) left := mid + 1; - case (#greater) right := mid; - case (#equal) return #found(indexByBlockElement(blockIndex, mid)) - } - }; - left - }; - - #insertionIndex(indexByBlockElement(blockIndex, elementIndex)) - }; - - /// Returns true iff every element in `list` satisfies `predicate`. - /// In particular, if `list` is empty the function returns `true`. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.all(list, func x { x > 1 }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func all(self : List, predicate : T -> Bool) : Bool { - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return true; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (not predicate(x)) return false; - case null return true - }; - j += 1 - }; - i += 1 - }; - true - }; - - /// Returns true iff some element in `list` satisfies `predicate`. - /// In particular, if `list` is empty the function returns `false`. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.any(list, func x { x > 3 }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func any(self : List, predicate : T -> Bool) : Bool = findIndex(self, predicate) != null; - - /// Returns an Iterator (`Iter`) over the elements of a List. - /// Iterator provides a single method `next()`, which returns - /// elements in order, or `null` when out of elements to iterate over. - /// - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// - /// var sum = 0; - /// for (element in List.values(list)) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - public func values(self : List) : Types.Iter = object { - let blocks = self.blocks.size(); - var blockIndex = 0; - var elementIndex = 0; - var db : [var ?T] = self.blocks[blockIndex]; - var dbSize = db.size(); - - public func next() : ?T { - if (elementIndex == dbSize) { - blockIndex += 1; - if (blockIndex >= blocks) return null; - db := self.blocks[blockIndex]; - dbSize := db.size(); - if (dbSize == 0) return null; - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?x) { - elementIndex += 1; - return ?x - }; - case (_) return null - } - } - }; - - /// Returns an Iterator (`Iter`) over the items (index-value pairs) in the list. - /// Each item is a tuple of `(index, value)`. The iterator provides a single method - /// `next()` which returns elements in order, or `null` when out of elements. - /// - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// assert Iter.toArray(List.enumerate(list)) == [(0, 10), (1, 11), (2, 12)]; - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - /// - /// Warning: Allocates memory on the heap to store ?(Nat, T). - public func enumerate(self : List) : Types.Iter<(Nat, T)> = object { - let blocks = self.blocks.size(); - var blockIndex = 0; - var elementIndex = 0; - var size = 0; - var db : [var ?T] = [var]; - var i = 0; - - public func next() : ?(Nat, T) { - if (elementIndex == size) { - blockIndex += 1; - if (blockIndex >= blocks) return null; - db := self.blocks[blockIndex]; - size := db.size(); - if (size == 0) return null; - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?x) { - let ret = ?(i, x); - elementIndex += 1; - i += 1; - return ret - }; - case (_) return null - } - } - }; - - /// Returns an Iterator (`Iter`) over the elements of the list in reverse order. - /// The iterator provides a single method `next()` which returns elements from - /// last to first, or `null` when out of elements. - /// - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// - /// var sum = 0; - /// for (element in List.reverseValues(list)) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - public func reverseValues(self : List) : Types.Iter = object { - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex; - var db : [var ?T] = if (blockIndex < self.blocks.size()) { - self.blocks[blockIndex] - } else { [var] }; - - public func next() : ?T { - if (elementIndex != 0) { - elementIndex -= 1 - } else { - blockIndex -= 1; - if (blockIndex == 0) return null; - db := self.blocks[blockIndex]; - elementIndex := db.size() - 1 - }; - - db[elementIndex] - } - }; - - /// Returns an Iterator (`Iter`) over the items in reverse order, i.e. pairs of index and value. - /// Iterator provides a single method `next()`, which returns - /// elements in reverse order, or `null` when out of elements to iterate over. - /// - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// assert Iter.toArray(List.reverseEnumerate(list)) == [(2, 12), (1, 11), (0, 10)]; - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - /// - /// Warning: Allocates memory on the heap to store ?(T, Nat). - public func reverseEnumerate(self : List) : Types.Iter<(Nat, T)> = object { - var i = size(self); - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex; - var db : [var ?T] = if (blockIndex < self.blocks.size()) { - self.blocks[blockIndex] - } else { [var] }; - - public func next() : ?(Nat, T) { - if (elementIndex != 0) { - elementIndex -= 1 - } else { - blockIndex -= 1; - if (blockIndex == 0) return null; - db := self.blocks[blockIndex]; - elementIndex := db.size() - 1 - }; - switch (db[elementIndex]) { - case (?x) { - i -= 1; - return ?(i, x) - }; - case (_) Prim.trap INTERNAL_ERROR - } - } - }; - - /// Returns an Iterator (`Iter`) over the indices (keys) of the list. - /// The iterator provides a single method `next()` which returns indices - /// from 0 to size-1, or `null` when out of elements. - /// - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let list = List.empty(); - /// List.add(list, "A"); - /// List.add(list, "B"); - /// List.add(list, "C"); - /// Iter.toArray(List.keys(list)) // [0, 1, 2] - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - public func keys(self : List) : Types.Iter = Nat.range(0, size(self)); - - /// Creates a new List containing all elements from the provided iterator. - /// Elements are added in the order they are returned by the iterator. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [1, 1, 1]; - /// let iter = array.vals(); - /// - /// let list = List.fromIter(iter); - /// assert Iter.toArray(List.values(list)) == [1, 1, 1]; - /// ``` - /// - /// Runtime: `O(size)` - public func fromIter(iter : Types.Iter) : List { - let list = empty(); - for (element in iter) add(list, element); - list - }; - - /// Convert an iterator to a new mutable List. - /// Elements are added in the order they are returned by the iterator. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [1, 1, 1]; - /// let iter = array.vals(); - /// - /// let list = iter.toList(); - /// assert Iter.toArray(List.values(list)) == [1, 1, 1]; - /// ``` - /// - /// Runtime: `O(size)` - public func toList(self : Types.Iter) : List { - fromIter(self) - }; - - /// Appends all elements from `added` to the end of `list`. - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2]); - /// let added = List.fromArray([3, 4]); - /// List.append(list, added); - /// assert List.toArray(list) == [1, 2, 3, 4]; - /// ``` - /// - /// Runtime: `O(size(added))` - /// - /// Space: `O(size(added))` - public func append(self : List, added : List) { - reserve(self, size(added)); - - let blocks = added.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) addUnsafe(self, x); - case null return - }; - j += 1 - }; - i += 1 - } - }; - - /// Adds all elements from the provided iterator to the end of the list. - /// Elements are added in the order they are returned by the iterator. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [1, 1, 1]; - /// let iter = array.vals(); - /// let list = List.repeat(2, 1); - /// - /// List.addAll(list, iter); - /// assert Iter.toArray(List.values(list)) == [2, 1, 1, 1]; - /// ``` - /// - /// The maximum number of elements in a `List` is 2^32. - /// - /// Runtime: `O(size)`, where n is the size of iter. - public func addAll(self : List, iter : Types.Iter) { - for (element in iter) add(self, element) - }; - - /// Creates a new immutable array containing all elements from the list. - /// Elements appear in the same order as in the list. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3]); - /// - /// assert List.toArray(list) == [1, 2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - public func toArray(self : List) : [T] { - var blockIndex = 0; - var elementIndex = 0; - var sz = 0; - var db : [var ?T] = [var]; - - func generator(_ : Nat) : T { - if (elementIndex == sz) { - blockIndex += 1; - db := self.blocks[blockIndex]; - sz := db.size(); - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?x) { - elementIndex += 1; - return x - }; - case (_) Prim.trap INTERNAL_ERROR - } - }; - - Array.tabulate(size(self), generator) - }; - - /// Creates a List containing elements from an Array. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [2, 3]; - /// let list = List.fromArray(array); - /// assert Iter.toArray(List.values(list)) == [2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - public func fromArray(array : [T]) : List { - let (blockIndex, elementIndex) = locate(array.size()); - - let blocks = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) { blockIndex - 1 } else blockIndex)); - let dataBlocks = VarArray.repeat<[var ?T]>([var], blocks); - - var i = 1; - var pos = 0; - - while (i < blockIndex) { - let len = dataBlockSize(i); - dataBlocks[i] := VarArray.tabulate(len, func i = ?array[pos + i]); - pos += len; - i += 1 - }; - if (elementIndex != 0 and blockIndex < blocks) { - dataBlocks[i] := VarArray.tabulate( - dataBlockSize(i), - func i = if (i < elementIndex) ?array[pos + i] else null - ) - }; - - { - var blocks = dataBlocks; - var blockIndex = blockIndex; - var elementIndex = elementIndex - } - }; - - /// Creates a new mutable array containing all elements from the list. - /// Elements appear in the same order as in the list. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// let varArray = List.toVarArray(list); - /// assert Array.fromVarArray(varArray) == [1, 2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - public func toVarArray(self : List) : [var T] { - let ?fs = first(self) else return [var]; - - let array = VarArray.repeat(fs, size(self)); - - var index = 0; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return array; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) array[index] := x; - case null return array - }; - j += 1; - index += 1 - }; - i += 1 - }; - array - }; - - /// Creates a new List containing all elements from the mutable array. - /// Elements appear in the same order as in the array. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [var 2, 3]; - /// let list = List.fromVarArray(array); - /// assert Iter.toArray(List.values(list)) == [2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - public func fromVarArray(array : [var T]) : List { - let (blockIndex, elementIndex) = locate(array.size()); - - let blocks = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) { blockIndex - 1 } else blockIndex)); - let dataBlocks = VarArray.repeat<[var ?T]>([var], blocks); - - func makeBlock(array : [var T], p : Nat, len : Nat, fill : Nat) : [var ?T] { - let block = VarArray.repeat(null, len); - var j = 0; - var pos = p; - while (j < fill) { - block[j] := ?array[pos]; - j += 1; - pos += 1 - }; - block - }; - - var i = 1; - var pos = 0; - - while (i < blockIndex) { - let len = dataBlockSize(i); - dataBlocks[i] := makeBlock(array, pos, len, len); - pos += len; - i += 1 - }; - if (elementIndex != 0) { - dataBlocks[i] := makeBlock(array, pos, dataBlockSize(i), elementIndex) - }; - - { - var blocks = dataBlocks; - var blockIndex = blockIndex; - var elementIndex = elementIndex - } - }; - - /// Returns the first element of `list`, or `null` if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// assert List.first(List.fromArray([1, 2, 3])) == ?1; - /// assert List.first(List.empty()) == null; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func first(self : List) : ?T { - if (self.blockIndex == 1) null else self.blocks[1][0] - }; - - /// Returns the last element of `list`, or `null` if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// assert List.last(List.fromArray([1, 2, 3])) == ?3; - /// assert List.last(List.empty()) == null; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func last(self : List) : ?T { - let e = self.elementIndex; - if (e > 0) return self.blocks[self.blockIndex][e - 1]; - - let b = self.blockIndex - 1 : Nat; - if (b == 0) null else { - let block = self.blocks[b]; - block[block.size() - 1] - } - }; - - /// Applies `f` to each element in `list`. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// List.forEach(list, func(x) { - /// Debug.print(Nat.toText(x)); // prints each element in list - /// }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEach(self : List, f : T -> ()) { - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) f(x); - case null return - }; - j += 1 - }; - i += 1 - } - }; - - /// Applies `f` to each item `(i, x)` in `list` where `i` is the key - /// and `x` is the value. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// List.forEachEntry(list, func (i,x) { - /// // prints each item (i,x) in list - /// Debug.print(Nat.toText(i) # Nat.toText(x)); - /// }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEachEntry(self : List, f : (Nat, T) -> ()) { - var index = 0; - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) f(index, x); - case null return - }; - j += 1; - index += 1 - }; - i += 1 - } - }; - - func actualInterval(fromInclusive : Int, toExclusive : Int, size : Nat) : (Nat, Nat) { - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - (Prim.abs(startInt), Prim.abs(endInt)) - }; - - /// Returns an iterator over a slice of `list` starting at `fromInclusive` up to (but not including) `toExclusive`. - /// - /// Negative indices are relative to the end of the list. For example, `-1` corresponds to the last element in the list. - /// - /// If the indices are out of bounds, they are clamped to the list bounds. - /// If the first index is greater than the second, the function returns an empty iterator. - /// - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4, 5]); - /// let iter1 = List.range(list, 3, List.size(list)); - /// assert iter1.next() == ?4; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// let iter2 = List.range(list, 3, -1); - /// assert iter2.next() == ?4; - /// assert iter2.next() == null; - /// - /// let iter3 = List.range(list, 0, 0); - /// assert iter3.next() == null; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func range(self : List, fromInclusive : Int, toExclusive : Int) : Types.Iter = object { - let (start, end) = actualInterval(fromInclusive, toExclusive, size(self)); - let blocks = self.blocks.size(); - var blockIndex = 0; - var elementIndex = 0; - if (start != 0) { - let (block, element) = locate(start - 1); - blockIndex := block; - elementIndex := element + 1 - }; - var db : [var ?T] = self.blocks[blockIndex]; - var dbSize = db.size(); - var index = fromInclusive; - - public func next() : ?T { - if (index >= end) return null; - index += 1; - - if (elementIndex == dbSize) { - blockIndex += 1; - if (blockIndex >= blocks) return null; - db := self.blocks[blockIndex]; - dbSize := db.size(); - if (dbSize == 0) return null; - elementIndex := 0 - }; - let ret = db[elementIndex]; - elementIndex += 1; - ret - } - }; - - func sliceToArrayBase(self : List, start : Nat) : { - next(i : Nat) : T - } = object { - var blockIndex = 0; - var elementIndex = 0; - if (start != 0) { - let (block, element) = locate(start - 1); - blockIndex := block; - elementIndex := element + 1 - }; - var db : [var ?T] = self.blocks[blockIndex]; - var dbSize = db.size(); - - public func next(i : Nat) : T { - if (elementIndex == dbSize) { - blockIndex += 1; - db := self.blocks[blockIndex]; - dbSize := db.size(); - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?x) { - elementIndex += 1; - return x - }; - case null Prim.trap INTERNAL_ERROR - } - } - }; - - /// Returns a new array containing elements from `list` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// let array = List.fromArray([1, 2, 3, 4, 5]); - /// - /// let slice1 = List.sliceToArray(array, 1, 4); - /// assert slice1 == [2, 3, 4]; - /// - /// let slice2 = List.sliceToArray(array, 1, -1); - /// assert slice2 == [2, 3, 4]; - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToArray(self : List, fromInclusive : Int, toExclusive : Int) : [T] { - let (start, end) = actualInterval(fromInclusive, toExclusive, size(self)); - Array.tabulate(end - start, sliceToArrayBase(self, start).next) - }; - - /// Returns a new var array containing elements from `list` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// import VarArray "mo:core/VarArray"; - /// import Nat "mo:core/Nat"; - /// - /// let array = List.fromArray([1, 2, 3, 4, 5]); - /// - /// let slice1 = List.sliceToVarArray(array, 1, 4); - /// assert VarArray.equal(slice1, [var 2, 3, 4], Nat.equal); - /// - /// let slice2 = List.sliceToVarArray(array, 1, -1); - /// assert VarArray.equal(slice2, [var 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToVarArray(self : List, fromInclusive : Int, toExclusive : Int) : [var T] { - let (start, end) = actualInterval(fromInclusive, toExclusive, size(self)); - VarArray.tabulate(end - start, sliceToArrayBase(self, start).next) - }; - - /// Like `forEachEntryRev` but iterates through the list in reverse order, - /// from end to beginning. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// List.reverseForEachEntry(list, func (i,x) { - /// // prints each item (i,x) in list - /// Debug.print(Nat.toText(i) # Nat.toText(x)); - /// }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func reverseForEachEntry(self : List, f : (Nat, T) -> ()) { - var index = 0; - - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) f(index, x); - case null Prim.trap INTERNAL_ERROR - }; - index += 1 - }; - i -= 1 - } - }; - - /// Applies `f` to each element in `list` in reverse order. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// List.reverseForEach(list, func (x) { - /// Debug.print(Nat.toText(x)); // prints each element in list in reverse order - /// }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func reverseForEach(self : List, f : T -> ()) { - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) f(x); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - } - }; - - /// Executes the closure over a slice of `list` starting at `fromInclusive` up to (but not including) `toExclusive`. - /// - /// ```motoko include=import - /// import Debug "mo:core/Debug"; - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 2, 3, 4, 5]); - /// List.forEachInRange(list, func x = Debug.print(Nat.toText(x)), 1, 2); // prints 2 and 3 - /// ``` - /// - /// Runtime: `O(toExclusive - fromExclusive)` - /// - /// Space: `O(1)` - public func forEachInRange(self : List, f : T -> (), fromInclusive : Nat, toExclusive : Nat) { - if (not (fromInclusive <= toExclusive and toExclusive <= size(self))) Prim.trap("Invalid range"); - - func traverseBlock(block : [var ?T], f : T -> (), from : Nat, to : Nat) { - var i = from; - while (i < to) { - switch (block[i]) { - case (?value) f(value); - case null Prim.trap(INTERNAL_ERROR) - }; - i += 1 - } - }; - - let (fromBlock, fromElement) = locate(fromInclusive); - let (toBlock, toElement) = locate(toExclusive); - - let blocks = self.blocks; - let sz = blocks.size(); - - if (fromBlock == toBlock) { - if (fromBlock < sz) traverseBlock(blocks[fromBlock], f, fromElement, toElement); - return - }; - - traverseBlock(blocks[fromBlock], f, fromElement, blocks[fromBlock].size()); - - var i = fromBlock + 1; - let to = Nat.min(toBlock, sz); - while (i < to) { - traverseBlock(blocks[i], f, 0, blocks[i].size()); - i += 1 - }; - - if (toBlock < sz) traverseBlock(blocks[toBlock], f, 0, toElement) - }; - - /// Returns true if the list contains the specified element according to the provided - /// equality function. Uses the provided `equal` function to compare elements. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 2); - /// List.add(list, 0); - /// List.add(list, 3); - /// - /// assert List.contains(list, Nat.equal, 2); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func contains(self : List, equal : (implicit : (T, T) -> Bool), element : T) : Bool { - Option.isSome(indexOf(self, equal, element)) - }; - - /// Returns the greatest element in the list according to the ordering defined by `compare`. - /// Returns `null` if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// - /// assert List.max(list, Nat.compare) == ?2; - /// assert List.max(List.empty(), Nat.compare) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func max(self : List, compare : (implicit : (T, T) -> Types.Order)) : ?T { - var maxSoFar : T = switch (first(self)) { - case (?x) x; - case null return null - }; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return ?maxSoFar; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) switch (compare(x, maxSoFar)) { - case (#greater) maxSoFar := x; - case _ {} - }; - case null return ?maxSoFar - }; - j += 1 - }; - i += 1 - }; - - ?maxSoFar - }; - - /// Returns the least element in the list according to the ordering defined by `compare`. - /// Returns `null` if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// - /// assert List.min(list, Nat.compare) == ?1; - /// assert List.min(List.empty(), Nat.compare) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func min(self : List, compare : (implicit : (T, T) -> Types.Order)) : ?T { - var minSoFar : T = switch (first(self)) { - case (?x) x; - case null return null - }; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return ?minSoFar; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) switch (compare(x, minSoFar)) { - case (#less) minSoFar := x; - case _ {} - }; - case null return ?minSoFar - }; - j += 1 - }; - i += 1 - }; - - ?minSoFar - }; - - /// Tests if two lists are equal by comparing their elements using the provided `equal` function. - /// Returns true if and only if both lists have the same size and all corresponding elements - /// are equal according to the provided function. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list1 = List.fromArray([1,2]); - /// let list2 = List.empty(); - /// List.add(list2, 1); - /// List.add(list2, 2); - /// - /// assert List.equal(list1, list2, Nat.equal); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func equal(self : List, other : List, equal : (implicit : (T, T) -> Bool)) : Bool { - if (size(self) != size(other)) return false; - - let blocks1 = self.blocks; - let blocks2 = other.blocks; - let blockCount = Nat.min(blocks1.size(), blocks2.size()); - - var i = 1; - while (i < blockCount) { - let db1 = blocks1[i]; - let db2 = blocks2[i]; - let sz = Nat.min(db1.size(), db2.size()); - if (sz == 0) return true; - - var j = 0; - while (j < sz) { - switch (db1[j], db2[j]) { - case (?x, ?y) if (not equal(x, y)) return false; - case (_, _) return true - }; - j += 1 - }; - i += 1 - }; - return true - }; - - /// Compares two lists lexicographically using the provided `compare` function. - /// Elements are compared pairwise until a difference is found or one list ends. - /// If all elements compare equal, the shorter list is considered less than the longer list. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list1 = List.fromArray([0, 1]); - /// let list2 = List.fromArray([2]); - /// let list3 = List.fromArray([0, 1, 2]); - /// - /// assert List.compare(list1, list2, Nat.compare) == #less; - /// assert List.compare(list1, list3, Nat.compare) == #less; - /// assert List.compare(list2, list3, Nat.compare) == #greater; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func compare(self : List, other : List, compare : (implicit : (T, T) -> Types.Order)) : Types.Order { - let blocks1 = self.blocks; - let blocks2 = other.blocks; - let blockCount = Nat.min(blocks1.size(), blocks2.size()); - - var i = 1; - label l while (i < blockCount) { - let db1 = blocks1[i]; - let db2 = blocks2[i]; - let sz = Nat.min(db1.size(), db2.size()); - if (sz == 0) break l; - - var j = 0; - while (j < sz) { - switch (db1[j], db2[j]) { - case (?x, ?y) switch (compare(x, y)) { - case (#less) return #less; - case (#greater) return #greater; - case _ {} - }; - case (_, _) break l - }; - j += 1 - }; - i += 1 - }; - return Nat.compare(size(self), size(other)) - }; - - /// Creates a textual representation of `list`, using `toText` to recursively - /// convert the elements into Text. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1,2,3,4]); - /// - /// assert List.toText(list, Nat.toText) == "List[1, 2, 3, 4]"; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `toText` runs in O(1) time and space. - public func toText(self : List, toText : (implicit : T -> Text)) : Text { - var text = switch (first(self)) { - case (?x) toText(x); - case null "" - }; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - label l while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) break l; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) text #= ", " # toText(x); - case null break l - }; - j += 1 - }; - i += 1 - }; - - "List[" # text # "]" - }; - - /// Collapses the elements in `list` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1,2,3]); - /// - /// assert List.foldLeft(list, "", func (acc, x) { acc # Nat.toText(x)}) == "123"; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `combine` runs in O(1)` time and space. - public func foldLeft(self : List, base : A, combine : (A, T) -> A) : A { - var accumulation = base; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return accumulation; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) accumulation := combine(accumulation, x); - case null return accumulation - }; - j += 1 - }; - i += 1 - }; - accumulation - }; - - /// Collapses the elements in `list` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1,2,3]); - /// - /// assert List.foldRight(list, "", func (x, acc) { Nat.toText(x) # acc }) == "123"; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `combine` runs in O(1)` time and space. - public func foldRight(self : List, base : A, combine : (T, A) -> A) : A { - var accumulation = base; - - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) accumulation := combine(x, accumulation); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - }; - - accumulation - }; - - /// Reverses the order of elements in `list` by overwriting in place. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let list = List.fromArray([1,2,3]); - /// - /// List.reverseInPlace(list); - /// assert Iter.toArray(List.values(list)) == [3, 2, 1]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func reverseInPlace(self : List) { - let vsize = size(self); - if (vsize <= 1) return; - - let (finalBlock, finalElement) = locate(vsize / 2); - - let blocks = self.blocks; - - var blockIndexBack = self.blockIndex; - var elementIndexBack = self.elementIndex; - var dbBack : [var ?T] = if (blockIndexBack < self.blocks.size()) { - self.blocks[blockIndexBack] - } else { [var] }; - - var i = 1; - var index = 0; - while (i <= finalBlock) { - let db = blocks[i]; - let sz = if (i == finalBlock) finalElement else db.size(); - - var j = 0; - while (j < sz) { - if (elementIndexBack == 0) { - blockIndexBack -= 1; - dbBack := self.blocks[blockIndexBack]; - elementIndexBack := dbBack.size() - 1 - } else { - elementIndexBack -= 1 - }; - - let temp = db[j]; - db[j] := dbBack[elementIndexBack]; - dbBack[elementIndexBack] := temp; - - j += 1; - index += 1 - }; - i += 1 - } - }; - - /// Returns a new List with the elements from `list` in reverse order. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let list = List.fromArray([1,2,3]); - /// - /// let rlist = List.reverse(list); - /// assert Iter.toArray(List.values(rlist)) == [3, 2, 1]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func reverse(self : List) : List { - let rlist = repeatInternal(null, size(self)); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var blockIndexBack = rlist.blockIndex; - var elementIndexBack = rlist.elementIndex; - var dbBack : [var ?T] = if (blockIndexBack < rlist.blocks.size()) { - rlist.blocks[blockIndexBack] - } else { [var] }; - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return rlist; - - var j = 0; - while (j < sz) { - if (elementIndexBack == 0) { - blockIndexBack -= 1; - if (blockIndexBack == 0) return rlist; - dbBack := rlist.blocks[blockIndexBack]; - elementIndexBack := dbBack.size() - 1 - } else { - elementIndexBack -= 1 - }; - - dbBack[elementIndexBack] := db[j]; - j += 1 - }; - i += 1 - }; - rlist - }; - - /// Returns true if and only if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([2,0,3]); - /// assert not List.isEmpty(list); - /// assert List.isEmpty(List.empty()); - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func isEmpty(self : List) : Bool { - self.blockIndex == 1 - }; - - /// Unsafe iterator starting from `start`. - /// - /// Example: - /// ``` - /// let list = List.fromArray([1, 2, 3, 4, 5]); - /// let reader = List.reader(list, 2); - /// assert reader() == 3; - /// assert reader() == 4; - /// assert reader() == 5; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func reader(self : List, start : Nat) : () -> T { - var blockIndex = 0; - var elementIndex = 0; - if (start != 0) { - let (block, element) = locate(start - 1); - blockIndex := block; - elementIndex := element + 1 - }; - var db : [var ?T] = self.blocks[blockIndex]; - var dbSize = db.size(); - func next() : T { - // Note: next() traps when reading beyond end of list - if (elementIndex == dbSize) { - blockIndex += 1; - db := self.blocks[blockIndex]; - dbSize := db.size(); - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?ret) { - elementIndex += 1; - return ret - }; - case (_) Prim.trap("List.reader(): out of bounds") - } - }; - next - }; - -} diff --git a/.mops/core@2.4.0/src/Map.mo b/.mops/core@2.4.0/src/Map.mo deleted file mode 100644 index 6e10175..0000000 --- a/.mops/core@2.4.0/src/Map.mo +++ /dev/null @@ -1,2672 +0,0 @@ -/// An imperative key-value map based on order/comparison of the keys. -/// The map data structure type is stable and can be used for orthogonal persistence. -/// -/// Example: -/// ```motoko -/// import Map "mo:core/Map"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// // creation -/// let map = Map.empty(); -/// // insertion -/// Map.add(map, Nat.compare, 0, "Zero"); -/// // retrieval -/// assert Map.get(map, Nat.compare, 0) == ?"Zero"; -/// assert Map.get(map, Nat.compare, 1) == null; -/// // removal -/// Map.remove(map, Nat.compare, 0); -/// assert Map.isEmpty(map); -/// } -/// ``` -/// -/// The internal implementation is a B-tree with order 32. -/// -/// Performance: -/// * Runtime: `O(log(n))` worst case cost per insertion, removal, and retrieval operation. -/// * Space: `O(n)` for storing the entire map. -/// `n` denotes the number of key-value entries stored in the map. - -// Data structure implementation is courtesy of Byron Becker. -// Source: https://github.com/canscale/StableHeapBTreeMap -// Copyright (c) 2022 Byron Becker. -// Distributed under Apache 2.0 license. -// With adjustments by the Motoko team. - -import PureMap "pure/Map"; -import Types "Types"; -import Iter "Iter"; -import Order "Order"; -import VarArray "VarArray"; -import Runtime "Runtime"; -import Stack "Stack"; -import Option "Option"; -import BTreeHelper "internal/BTreeHelper"; - -module { - let btreeOrder = 32; // Should be >= 4 and <= 512. - - public type Map = Types.Map; - - type Node = Types.Map.Node; - type Data = Types.Map.Data; - type Internal = Types.Map.Internal; - type Leaf = Types.Map.Leaf; - - /// Convert the mutable key-value map to an immutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import PureMap "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), Nat.compare); - /// let pureMap = Map.toPure(map, Nat.compare); - /// assert Iter.toArray(PureMap.entries(pureMap)) == Iter.toArray(Map.entries(map)) - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - /// @deprecated M0235 - public func toPure(self : Map, compare : (implicit : (K, K) -> Order.Order)) : PureMap.Map { - PureMap.fromIter(entries(self), compare) - }; - - /// Convert an immutable key-value map to a mutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import PureMap "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let pureMap = PureMap.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), Nat.compare); - /// let map = Map.fromPure(pureMap, Nat.compare); - /// assert Iter.toArray(Map.entries(map)) == Iter.toArray(PureMap.entries(pureMap)) - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func fromPure(map : PureMap.Map, compare : (implicit : (K, K) -> Order.Order)) : Map { - fromIter(PureMap.entries(map), compare) - }; - - /// Create a copy of the mutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let originalMap = Map.fromIter( - /// [(1, "One"), (2, "Two"), (3, "Three")].values(), Nat.compare); - /// let clonedMap = Map.clone(originalMap); - /// Map.add(originalMap, Nat.compare, 4, "Four"); - /// assert Map.size(clonedMap) == 3; - /// assert Map.size(originalMap) == 4; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func clone(self : Map) : Map { - { - var root = cloneNode(self.root); - var size = self.size - } - }; - - /// Create a new empty mutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// assert Map.size(map) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Map { - { - var root = #leaf({ - data = { - kvs = VarArray.repeat(null, btreeOrder - 1); - var count = 0 - } - }); - var size = 0 - } - }; - - /// Create a new mutable key-value map with a single entry. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.singleton(0, "Zero"); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero")]; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func singleton(key : K, value : V) : Map { - let kvs = VarArray.repeat(null, btreeOrder - 1); - kvs[0] := ?(key, value); - { - var root = #leaf { data = { kvs; var count = 1 } }; - var size = 1 - } - }; - - /// Delete all the entries in the key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert Map.size(map) == 3; - /// - /// Map.clear(map); - /// assert Map.size(map) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func clear(self : Map) { - let emptyMap = empty(); - self.root := emptyMap.root; - self.size := 0 - }; - - /// Determines whether a key-value map is empty. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert not Map.isEmpty(map); - /// Map.clear(map); - /// assert Map.isEmpty(map); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func isEmpty(self : Map) : Bool { - self.size == 0 - }; - - /// Return the number of entries in a key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert Map.size(map) == 3; - /// Map.clear(map); - /// assert Map.size(map) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func size(self : Map) : Nat { - self.size - }; - - /// Test whether two imperative maps have equal entries. - /// Both maps have to be constructed by the same comparison function. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let map1 = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// let map2 = Map.clone(map1); - /// - /// assert Map.equal(map1, map2, Nat.compare, Text.equal); - /// Map.clear(map2); - /// assert not Map.equal(map1, map2, Nat.compare, Text.equal); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func equal(self : Map, other : Map, compare : (implicit : (K, K) -> Types.Order), equal : (implicit : (V, V) -> Bool)) : Bool { - if (size(self) != size(other)) { - return false - }; - let iterator1 = entries(self); - let iterator2 = entries(other); - loop { - let next1 = iterator1.next(); - let next2 = iterator2.next(); - switch (next1, next2) { - case (null, null) { - return true - }; - case (?(key1, value1), ?(key2, value2)) { - if ( - not (compare(key1, key2) == #equal) or - not equal(value1, value2) - ) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Tests whether the map contains the provided key. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert Map.containsKey(map, Nat.compare, 1); - /// assert not Map.containsKey(map, Nat.compare, 3); - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func containsKey(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : Bool { - Option.isSome(get(self, compare, key)) - }; - - /// Get the value associated with key in the given map if present and `null` otherwise. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert Map.get(map, Nat.compare, 1) == ?"One"; - /// assert Map.get(map, Nat.compare, 3) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func get(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : ?V { - switch (self.root) { - case (#internal(internalNode)) { - getFromInternal(internalNode, compare, key) - }; - case (#leaf(leafNode)) { getFromLeaf(leafNode, compare, key) } - } - }; - - /// Given `map` ordered by `compare`, insert a new mapping from `key` to `value`. - /// Replaces any existing entry under `key`. - /// Returns true if the key is new to the map, otherwise false. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// assert Map.insert(map, Nat.compare, 0, "Zero"); - /// assert Map.insert(map, Nat.compare, 1, "One"); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One")]; - /// assert not Map.insert(map, Nat.compare, 0, "Nil"); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Nil"), (1, "One")] - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func insert(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : Bool { - switch (swap(self, compare, key, value)) { - case null true; - case _ false - } - }; - - /// Given `map` ordered by `compare`, add a mapping from `key` to `value` to `map`. - /// Replaces any existing entry for `key`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// - /// Map.add(map, Nat.compare, 0, "Zero"); - /// Map.add(map, Nat.compare, 1, "One"); - /// Map.add(map, Nat.compare, 0, "Nil"); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Nil"), (1, "One")] - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func add(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) { - ignore swap(self, compare, key, value) - }; - - /// Associates the value with the key in the map. - /// If the key is not yet present in the map, a new key-value pair is added and `null` is returned. - /// Otherwise, if the key is already present, the value is overwritten and the previous value is returned. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.singleton(1, "One"); - /// - /// assert Map.swap(map, Nat.compare, 0, "Zero") == null; - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One")]; - /// - /// assert Map.swap(map, Nat.compare, 0, "Nil") == ?"Zero"; - /// assert Iter.toArray(Map.entries(map)) == [(0, "Nil"), (1, "One")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func swap(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : ?V { - let insertResult = switch (self.root) { - case (#leaf(leafNode)) { - leafInsertHelper(leafNode, btreeOrder, compare, key, value) - }; - case (#internal(internalNode)) { - internalInsertHelper(internalNode, btreeOrder, compare, key, value) - } - }; - - switch (insertResult) { - case (#insert(ov)) { - switch (ov) { - // if inserted a value that was not previously there, increment the tree size counter - case null { self.size += 1 }; - case _ {} - }; - ov - }; - case (#promote({ kv; leftChild; rightChild })) { - let kvs = VarArray.repeat(null, btreeOrder - 1); - kvs[0] := ?kv; - let children = VarArray.repeat>(null, btreeOrder); - children[0] := ?leftChild; - children[1] := ?rightChild; - self.root := #internal({ - data = { - kvs; - var count = 1 - }; - children - }); - // promotion always comes from inserting a new element, so increment the tree size counter - self.size += 1; - - null - } - } - }; - - /// Overwrites the value of an existing key and returns the previous value. - /// If the key does not exist, it has no effect and returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.singleton(0, "Zero"); - /// - /// let prev1 = Map.replace(map, Nat.compare, 0, "Nil"); // overwrites the value for existing key. - /// assert prev1 == ?"Zero"; - /// assert Map.get(map, Nat.compare, 0) == ?"Nil"; - /// - /// let prev2 = Map.replace(map, Nat.compare, 1, "One"); // no effect, key is absent - /// assert prev2 == null; - /// assert Map.get(map, Nat.compare, 1) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func replace(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : ?V { - // TODO: Could be optimized in future - if (containsKey(self, compare, key)) { - swap(self, compare, key, value) - } else { - null - } - }; - - /// Delete an entry by its key in the map. - /// No effect if the key is not present. - /// - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (2, "Two"), (1, "One")].values(), - /// Nat.compare); - /// - /// Map.remove(map, Nat.compare, 1); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (2, "Two")]; - /// Map.remove(map, Nat.compare, 42); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - public func remove(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) { - ignore delete(self, compare, key) - }; - - /// Delete an existing entry by its key in the map. - /// Returns `true` if the key was present in the map, otherwise `false`. - /// - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (2, "Two"), (1, "One")].values(), - /// Nat.compare); - /// - /// assert Map.delete(map, Nat.compare, 1); // present, returns true - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (2, "Two")]; - /// - /// assert not Map.delete(map, Nat.compare, 42); // absent, returns false - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// @deprecated M0235 - public func delete(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : Bool { - switch (take(self, compare, key)) { - case null false; - case _ true - } - }; - - /// Removes any existing entry by its key in the map. - /// Returns the previous value of the key or `null` if the key was absent. - /// - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (2, "Two"), (1, "One")].values(), - /// Nat.compare); - /// - /// assert Map.take(map, Nat.compare, 0) == ?"Zero"; - /// assert Iter.toArray(Map.entries(map)) == [(1, "One"), (2, "Two")]; - /// - /// assert Map.take(map, Nat.compare, 3) == null; - /// assert Iter.toArray(Map.entries(map)) == [(1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// @deprecated M0235 - public func take(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : ?V { - let deletedValue = switch (self.root) { - case (#leaf(leafNode)) { - // TODO: think about how this can be optimized so don't have to do two steps (search and then insert)? - switch (NodeUtil.getKeyIndex(leafNode.data, compare, key)) { - case (#keyFound(deleteIndex)) { - leafNode.data.count -= 1; - let (_, deletedValue) = BTreeHelper.deleteAndShift<(K, V)>(leafNode.data.kvs, deleteIndex); - self.size -= 1; - ?deletedValue - }; - case _ { null } - } - }; - case (#internal(internalNode)) { - let deletedValueResult = switch (internalDeleteHelper(internalNode, btreeOrder, compare, key, false)) { - case (#delete(value)) { value }; - case (#mergeChild({ internalChild; deletedValue })) { - if (internalChild.data.count > 0) { - self.root := #internal(internalChild) - } - // This case will be hit if the BTree has order == 4 - // In this case, the internalChild has no keys (last key was merged with new child), so need to promote that merged child (its only child) - else { - self.root := switch (internalChild.children[0]) { - case (?node) { node }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.delete(), element deletion failed, due to a null replacement node error") - } - } - }; - deletedValue - } - }; - switch (deletedValueResult) { - // if deleted a value from the BTree, decrement the size - case (?deletedValue) { self.size -= 1 }; - case null {} - }; - deletedValueResult - } - }; - deletedValue - }; - - public func toArray(self : Map) : [(K, V)] { - Iter.toArray(entries(self)) - }; - - public func toVarArray(self : Map) : [var (K, V)] { - Iter.toVarArray(entries(self)) - }; - - /// Retrieves the key-value pair from the map with the maximum key. - /// If the map is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// - /// assert Map.maxEntry(map) == null; - /// - /// Map.add(map, Nat.compare, 0, "Zero"); - /// Map.add(map, Nat.compare, 2, "Two"); - /// Map.add(map, Nat.compare, 1, "One"); - /// - /// assert Map.maxEntry(map) == ?(2, "Two") - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func maxEntry(self : Map) : ?(K, V) { - reverseEntries(self).next() - }; - - /// Retrieves the key-value pair from the map with the minimum key. - /// If the map is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// - /// assert Map.minEntry(map) == null; - /// - /// Map.add(map, Nat.compare, 2, "Two"); - /// Map.add(map, Nat.compare, 0, "Zero"); - /// Map.add(map, Nat.compare, 1, "One"); - /// - /// assert Map.minEntry(map) == ?(0, "Zero") - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func minEntry(self : Map) : ?(K, V) { - entries(self).next() - }; - - /// Returns an iterator over the key-value pairs in the map, - /// traversing the entries in the ascending order of the keys. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// var sum = 0; - /// var text = ""; - /// for ((k, v) in Map.entries(map)) { sum += k; text #= v }; - /// assert sum == 3; - /// assert text == "ZeroOneTwo" - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func entries(self : Map) : Types.Iter<(K, V)> { - switch (self.root) { - case (#leaf(leafNode)) { return leafEntries(leafNode) }; - case (#internal(internalNode)) { internalEntries(internalNode) } - } - }; - - /// Returns an iterator over the key-value pairs in the map, - /// starting from a given key in ascending order. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (3, "Three"), (1, "One")].values(), Nat.compare); - /// assert Iter.toArray(Map.entriesFrom(map, Nat.compare, 1)) == [(1, "One"), (3, "Three")]; - /// assert Iter.toArray(Map.entriesFrom(map, Nat.compare, 2)) == [(3, "Three")]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func entriesFrom( - self : Map, - compare : (implicit : (K, K) -> Order.Order), - key : K - ) : Types.Iter<(K, V)> { - switch (self.root) { - case (#leaf(leafNode)) leafEntriesFrom(leafNode, compare, key); - case (#internal(internalNode)) internalEntriesFrom(internalNode, compare, key) - } - }; - - /// Returns an iterator over the key-value pairs in the map, - /// traversing the entries in the descending order of the keys. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.reverseEntries(map)) == [(2, "Two"), (1, "One"), (0, "Zero")]; - /// var sum = 0; - /// var text = ""; - /// for ((k, v) in Map.reverseEntries(map)) { sum += k; text #= v }; - /// assert sum == 3; - /// assert text == "TwoOneZero" - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseEntries(self : Map) : Types.Iter<(K, V)> { - switch (self.root) { - case (#leaf(leafNode)) reverseLeafEntries(leafNode); - case (#internal(internalNode)) reverseInternalEntries(internalNode) - } - }; - - /// Returns an iterator over the key-value pairs in the map, - /// starting from a given key in descending order. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (1, "One"), (3, "Three")].values(), Nat.compare); - /// assert Iter.toArray(Map.reverseEntriesFrom(map, Nat.compare, 0)) == [(0, "Zero")]; - /// assert Iter.toArray(Map.reverseEntriesFrom(map, Nat.compare, 2)) == [(1, "One"), (0, "Zero")]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseEntriesFrom( - self : Map, - compare : (implicit : (K, K) -> Order.Order), - key : K - ) : Types.Iter<(K, V)> { - switch (self.root) { - case (#leaf(leafNode)) reverseLeafEntriesFrom(leafNode, compare, key); - case (#internal(internalNode)) reverseInternalEntriesFrom(internalNode, compare, key) - } - }; - - /// Returns an iterator over the keys in the map, - /// traversing all keys in ascending order. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.keys(map)) == [0, 1, 2]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func keys(self : Map) : Types.Iter { - object { - let iterator = entries(self); - - public func next() : ?K { - switch (iterator.next()) { - case null null; - case (?(key, _)) ?key - } - } - } - }; - - /// Returns an iterator over the values in the map, - /// traversing the values in the ascending order of the keys to which they are associated. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.values(map)) == ["Zero", "One", "Two"]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func values(self : Map) : Types.Iter { - object { - let iterator = entries(self); - - public func next() : ?V { - switch (iterator.next()) { - case null null; - case (?(_, value)) ?value - } - } - } - }; - - /// Create a mutable key-value map with the entries obtained from an iterator. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = - /// Iter.fromArray([(0, "Zero"), (2, "Two"), (1, "One")]); - /// - /// let map = Map.fromIter(iter, Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func fromIter(iter : Types.Iter<(K, V)>, compare : (implicit : (K, K) -> Order.Order)) : Map { - let map = empty(); - for ((key, value) in iter) { - add(map, compare, key, value) - }; - map - }; - - /// Converts an iterator of entries into a Map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = - /// Iter.fromArray([(0, "Zero"), (2, "Two"), (1, "One")]); - /// - /// let map = iter.toMap(Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func toMap(self : Types.Iter<(K, V)>, compare : (implicit : (K, K) -> Order.Order)) : Map { - fromIter(self, compare) - }; - - public func fromArray(array : [(K, V)], compare : (implicit : (K, K) -> Order.Order)) : Map { - fromIter(array.values(), compare) - }; - - public func fromVarArray(array : [var (K, V)], compare : (implicit : (K, K) -> Order.Order)) : Map { - fromIter(array.values(), compare) - }; - - /// Apply an operation on each key-value pair contained in the map. - /// The operation is applied in ascending order of the keys. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// var sum = 0; - /// var text = ""; - /// Map.forEach(map, func (key, value) { - /// sum += key; - /// text #= value; - /// }); - /// assert sum == 3; - /// assert text == "ZeroOneTwo"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func forEach(self : Map, operation : (K, V) -> ()) { - for (entry in entries(self)) { - operation(entry) - } - }; - - /// Filter entries in a new map. - /// Create a copy of the mutable map that only contains the key-value pairs - /// that fulfil the criterion function. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numberNames = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// let evenNames = Map.filter(numberNames, Nat.compare, func (key, value) { - /// key % 2 == 0 - /// }); - /// - /// assert Iter.toArray(Map.entries(evenNames)) == [(0, "Zero"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func filter(self : Map, compare : (implicit : (K, K) -> Order.Order), criterion : (K, V) -> Bool) : Map { - let result = empty(); - for ((key, value) in entries(self)) { - if (criterion(key, value)) { - add(result, compare, key, value) - } - }; - result - }; - - /// Project all values of the map in a new map. - /// Apply a mapping function to the values of each entry in the map and - /// collect the mapped entries in a new mutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func f(key : Nat, _val : Text) : Nat = key * 2; - /// - /// let resMap = Map.map(map, f); - /// - /// assert Iter.toArray(Map.entries(resMap)) == [(0, 0), (1, 2), (2, 4)]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func map(self : Map, project : (K, V1) -> V2) : Map { - { - var root = mapNode(self.root, project); - var size = self.size - } - }; - - /// Iterate all entries in ascending order of the keys, - /// and accumulate the entries by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func folder(accum : (Nat, Text), key : Nat, val : Text) : ((Nat, Text)) - /// = (key + accum.0, accum.1 # val); - /// - /// assert Map.foldLeft(map, (0, ""), folder) == (3, "ZeroOneTwo"); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func foldLeft( - self : Map, - base : A, - combine : (A, K, V) -> A - ) : A { - var accumulator = base; - for ((key, value) in entries(self)) { - accumulator := combine(accumulator, key, value) - }; - accumulator - }; - - /// Iterate all entries in descending order of the keys, - /// and accumulate the entries by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func folder(key : Nat, val : Text, accum : (Nat, Text)) : ((Nat, Text)) - /// = (key + accum.0, accum.1 # val); - /// - /// assert Map.foldRight(map, (0, ""), folder) == (3, "TwoOneZero"); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func foldRight( - self : Map, - base : A, - combine : (K, V, A) -> A - ) : A { - var accumulator = base; - for ((key, value) in reverseEntries(self)) { - accumulator := combine(key, value, accumulator) - }; - accumulator - }; - - /// Check whether all entries in the map fulfil a predicate function, i.e. - /// the predicate function returns `true` for all entries in the map. - /// Returns `true` for an empty map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "0"), (2, "2"), (1, "1")].values(), Nat.compare); - /// - /// assert Map.all(map, func (k, v) = v == Nat.toText(k)); - /// assert not Map.all(map, func (k, v) = k < 2); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func all(self : Map, predicate : (K, V) -> Bool) : Bool { - //TODO: optimize - for (entry in entries(self)) { - if (not predicate(entry)) { - return false - } - }; - true - }; - - /// Test if any key-value pair in `map` satisfies the given predicate `pred`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "0"), (2, "2"), (1, "1")].values(), Nat.compare); - /// - /// assert Map.any(map, func (k, v) = (k >= 0)); - /// assert not Map.any(map, func (k, v) = (k >= 3)); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func any(self : Map, predicate : (K, V) -> Bool) : Bool { - //TODO: optimize - for (entry in entries(self)) { - if (predicate(entry)) { - return true - } - }; - false - }; - - /// Filter all entries in the map by also applying a projection to the value. - /// Apply a mapping function `project` to all entries in the map and collect all - /// entries, for which the function returns a non-null new value. Collect all - /// non-discarded entries with the key and new value in a new mutable map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func f(key : Nat, val : Text) : ?Text { - /// if(key == 0) {null} - /// else { ?("Twenty " # val)} - /// }; - /// - /// let newMap = Map.filterMap(map, Nat.compare, f); - /// - /// assert Iter.toArray(Map.entries(newMap)) == [(1, "Twenty One"), (2, "Twenty Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func filterMap(self : Map, compare : (implicit : (K, K) -> Order.Order), project : (K, V1) -> ?V2) : Map { - let result = empty(); - for ((key, value1) in entries(self)) { - switch (project(key, value1)) { - case null {}; - case (?value2) add(result, compare, key, value2) - } - }; - result - }; - - /// Internal sanity check function. - /// Can be used to check that key/value pairs have been inserted with a consistent key comparison function. - /// Traps if the internal map structure is invalid. - /// @deprecated M0235 - public func assertValid(self : Map, compare : (implicit : (K, K) -> Order.Order)) { - func checkIteration(iterator : Types.Iter<(K, V)>, order : Order.Order) { - switch (iterator.next()) { - case null {}; - case (?first) { - var previous = first; - loop { - switch (iterator.next()) { - case null return; - case (?next) { - if (compare(previous.0, next.0) != order) { - Runtime.trap("Invalid order") - }; - previous := next - } - } - } - } - } - }; - checkIteration(entries(self), #less); - checkIteration(reverseEntries(self), #greater) - }; - - /// Generate a textual representation of all the entries in the map. - /// Primarily to be used for testing and debugging. - /// The keys and values are formatted according to `keyFormat` and `valueFormat`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// assert Map.toText(map, Nat.toText, func t { t }) == "Map{(0, Zero), (1, One), (2, Two)}"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that `keyFormat` and `valueFormat` have runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func toText(self : Map, keyFormat : (implicit : (toText : K -> Text)), valueFormat : (implicit : (toText : V -> Text))) : Text { - var text = "Map{"; - var sep = ""; - for ((key, value) in entries(self)) { - text #= sep # "(" # keyFormat(key) # ", " # valueFormat(value) # ")"; - sep := ", " - }; - text # "}" - }; - - /// Compare two maps by primarily comparing keys and secondarily values. - /// Both maps must have been created by the same key comparison function. - /// The two maps are iterated by the ascending order of their creation and - /// order is determined by the following rules: - /// Less: - /// `map1` is less than `map2` if: - /// * the pairwise iteration hits a entry pair `entry1` and `entry2` where - /// `entry1` is less than `entry2` and all preceding entry pairs are equal, or, - /// * `map1` is a strict prefix of `map2`, i.e. `map2` has more entries than `map1` - /// and all entries of `map1` occur at the beginning of iteration `map2`. - /// `entry1` is less than `entry2` if: - /// * the key of `entry1` is less than the key of `entry2`, or - /// * `entry1` and `entry2` have equal keys and the value of `entry1` is less than - /// the value of `entry2`. - /// Equal: - /// `map1` and `map2` have same series of equal entries by pairwise iteration. - /// Greater: - /// `map1` is neither less nor equal `map2`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let map1 = Map.fromIter([(0, "Zero"), (1, "One")].values(), Nat.compare); - /// let map2 = Map.fromIter([(0, "Zero"), (2, "Two")].values(), Nat.compare); - /// - /// assert Map.compare(map1, map2, Nat.compare, Text.compare) == #less; - /// assert Map.compare(map1, map1, Nat.compare, Text.compare) == #equal; - /// assert Map.compare(map2, map1, Nat.compare, Text.compare) == #greater - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that `compareKey` and `compareValue` have runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func compare(self : Map, other : Map, compareKey : (implicit : (compare : (K, K) -> Order.Order)), compareValue : (implicit : (compare : (V, V) -> Order.Order))) : Order.Order { - let iterator1 = entries(self); - let iterator2 = entries(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?(key1, value1), ?(key2, value2)) { - let keyComparison = compareKey(key1, key2); - if (keyComparison != #equal) { - return keyComparison - }; - let valueComparison = compareValue(value1, value2); - if (valueComparison != #equal) { - return valueComparison - } - } - } - } - }; - - func leafEntries({ data } : Leaf) : Types.Iter<(K, V)> { - var i : Nat = 0; - object { - public func next() : ?(K, V) { - if (i >= data.count) { - null - } else { - let res = data.kvs[i]; - i += 1; - res - } - } - } - }; - - func leafEntriesFrom({ data } : Leaf, compare : (K, K) -> Order.Order, key : K) : Types.Iter<(K, V)> { - var i = switch (BinarySearch.binarySearchNode(data.kvs, compare, key, data.count)) { - case (#keyFound(i)) i; - case (#notFound(i)) i - }; - object { - public func next() : ?(K, V) { - if (i >= data.count) { - null - } else { - let res = data.kvs[i]; - i += 1; - res - } - } - } - }; - - func reverseLeafEntries({ data } : Leaf) : Types.Iter<(K, V)> { - var i : Nat = data.count; - object { - public func next() : ?(K, V) { - if (i == 0) { - null - } else { - let res = data.kvs[i - 1]; - i -= 1; - res - } - } - } - }; - - func reverseLeafEntriesFrom({ data } : Leaf, compare : (K, K) -> Order.Order, key : K) : Types.Iter<(K, V)> { - var i = switch (BinarySearch.binarySearchNode(data.kvs, compare, key, data.count)) { - case (#keyFound(i)) i + 1; // +1 to include this key - case (#notFound(i)) i // i is the index of the first key greater than the search key, or count if all keys are less than the search key - }; - object { - public func next() : ?(K, V) { - if (i == 0) { - null - } else { - let res = data.kvs[i - 1]; - i -= 1; - res - } - } - } - }; - - // Cursor type that keeps track of the current node and the current key-value index in the node - type NodeCursor = { node : Node; kvIndex : Nat }; - - func internalEntries(internal : Internal) : Types.Iter<(K, V)> { - // The nodeCursorStack keeps track of the current node and the current key-value index in the node - // We use a stack here to push to/pop off the next node cursor to visit - let nodeCursorStack = initializeForwardNodeCursorStack(internal); - internalEntriesFromStack(nodeCursorStack) - }; - - func internalEntriesFrom(internal : Internal, compare : (K, K) -> Order.Order, key : K) : Types.Iter<(K, V)> { - let nodeCursorStack = initializeForwardNodeCursorStackFrom(internal, compare, key); - internalEntriesFromStack(nodeCursorStack) - }; - - func internalEntriesFromStack(nodeCursorStack : Stack.Stack>) : Types.Iter<(K, V)> { - object { - public func next() : ?(K, V) { - // pop the next node cursor off the stack - var nodeCursor = Stack.pop(nodeCursorStack); - switch (nodeCursor) { - case null { return null }; - case (?{ node; kvIndex }) { - switch (node) { - // if a leaf node, iterate through the leaf node's next key-value pair - case (#leaf(leafNode)) { - let lastKV = leafNode.data.count - 1 : Nat; - if (kvIndex > lastKV) { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalEntries(), leaf kvIndex out of bounds") - }; - - let currentKV = switch (leafNode.data.kvs[kvIndex]) { - case (?kv) { kv }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Map.internalEntries(), null key-value pair found in leaf node." - # "leafNode.data.count=" # debug_show (leafNode.data.count) # ", kvIndex=" # debug_show (kvIndex) - ) - } - }; - // if not at the last key-value pair, push the next key-value index of the leaf onto the stack and return the current key-value pair - if (kvIndex < lastKV) { - Stack.push( - nodeCursorStack, - { - node = #leaf(leafNode); - kvIndex = kvIndex + 1 : Nat - } - ) - }; - - // return the current key-value pair - ?currentKV - }; - // if an internal node - case (#internal(internalNode)) { - let lastKV = internalNode.data.count - 1 : Nat; - // Developer facing message in case of a bug - if (kvIndex > lastKV) { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalEntries(), internal kvIndex out of bounds") - }; - - let currentKV = switch (internalNode.data.kvs[kvIndex]) { - case (?kv) { kv }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Map.internalEntries(), null key-value pair found in internal node. " # - "internal.data.count=" # debug_show (internalNode.data.count) # ", kvIndex=" # debug_show (kvIndex) - ) - } - }; - - let nextCursor = { - node = #internal(internalNode); - kvIndex = kvIndex + 1 : Nat - }; - // if not the last key-value pair, push the next key-value index of the internal node onto the stack - if (kvIndex < lastKV) { - Stack.push(nodeCursorStack, nextCursor) - }; - // traverse the next child's min subtree and push the resulting node cursors onto the stack - // then return the current key-value pair of the internal node - traverseMinSubtreeIter(nodeCursorStack, nextCursor); - ?currentKV - } - } - } - } - } - } - }; - - func reverseInternalEntries(internal : Internal) : Types.Iter<(K, V)> { - // The nodeCursorStack keeps track of the current node and the current key-value index in the node - // We use a stack here to push to/pop off the next node cursor to visit - let nodeCursorStack = initializeReverseNodeCursorStack(internal); - reverseInternalEntriesFromStack(nodeCursorStack) - }; - - func reverseInternalEntriesFrom(internal : Internal, compare : (K, K) -> Order.Order, key : K) : Types.Iter<(K, V)> { - let nodeCursorStack = initializeReverseNodeCursorStackFrom(internal, compare, key); - reverseInternalEntriesFromStack(nodeCursorStack) - }; - - func reverseInternalEntriesFromStack(nodeCursorStack : Stack.Stack>) : Types.Iter<(K, V)> { - object { - public func next() : ?(K, V) { - // pop the next node cursor off the stack - var nodeCursor = Stack.pop(nodeCursorStack); - switch (nodeCursor) { - case null { return null }; - case (?{ node; kvIndex }) { - let firstKV = 0 : Nat; - assert (kvIndex > firstKV); - switch (node) { - // if a leaf node, reverse iterate through the leaf node's next key-value pair - case (#leaf(leafNode)) { - let currentKV = switch (leafNode.data.kvs[kvIndex - 1]) { - case (?kv) { kv }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Map.reverseInternalEntries(), null key-value pair found in leaf node." - # "leafNode.data.count=" # debug_show (leafNode.data.count) # ", kvIndex=" # debug_show (kvIndex) - ) - } - }; - // if not at the last key-value pair, push the previous key-value index of the leaf onto the stack and return the current key-value pair - if (kvIndex - 1 : Nat > firstKV) { - Stack.push( - nodeCursorStack, - { - node = #leaf(leafNode); - kvIndex = kvIndex - 1 : Nat - } - ) - }; - - // return the current key-value pair - ?currentKV - }; - // if an internal node - case (#internal(internalNode)) { - let currentKV = switch (internalNode.data.kvs[kvIndex - 1]) { - case (?kv) { kv }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Map.reverseInternalEntries(), null key-value pair found in internal node. " # - "internal.data.count=" # debug_show (internalNode.data.count) # ", kvIndex=" # debug_show (kvIndex) - ) - } - }; - - let previousCursor = { - node = #internal(internalNode); - kvIndex = kvIndex - 1 : Nat - }; - // if not the first key-value pair, push the previous key-value index of the internal node onto the stack - if (kvIndex - 1 : Nat > firstKV) { - Stack.push(nodeCursorStack, previousCursor) - }; - // traverse the previous child's max subtree and push the resulting node cursors onto the stack - // then return the current key-value pair of the internal node - traverseMaxSubtreeIter(nodeCursorStack, previousCursor); - ?currentKV - } - } - } - } - } - } - }; - - func initializeForwardNodeCursorStack(internal : Internal) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - kvIndex = 0 - }; - - // push the initial cursor to the stack - Stack.push(nodeCursorStack, nodeCursor); - // then traverse left - traverseMinSubtreeIter(nodeCursorStack, nodeCursor); - nodeCursorStack - }; - - func initializeForwardNodeCursorStackFrom(internal : Internal, compare : (K, K) -> Order.Order, key : K) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - kvIndex = 0 - }; - - traverseMinSubtreeIterFrom(nodeCursorStack, nodeCursor, compare, key); - nodeCursorStack - }; - - func initializeReverseNodeCursorStack(internal : Internal) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - kvIndex = internal.data.count - }; - - // push the initial cursor to the stack - Stack.push(nodeCursorStack, nodeCursor); - // then traverse left - traverseMaxSubtreeIter(nodeCursorStack, nodeCursor); - nodeCursorStack - }; - - func initializeReverseNodeCursorStackFrom(internal : Internal, compare : (K, K) -> Order.Order, key : K) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - kvIndex = internal.data.count - }; - - traverseMaxSubtreeIterFrom(nodeCursorStack, nodeCursor, compare, key); - nodeCursorStack - }; - - // traverse the min subtree of the current node cursor, passing each new element to the node cursor stack - func traverseMinSubtreeIter(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor) { - var currentNode = nodeCursor.node; - var childIndex = nodeCursor.kvIndex; - - label l loop { - switch (currentNode) { - // If currentNode is leaf, have hit the minimum element of the subtree and already pushed it's cursor to the stack - // so can return - case (#leaf(_)) { - return - }; - // If currentNode is internal, add it's left most child to the stack and continue traversing - case (#internal(internalNode)) { - switch (internalNode.children[childIndex]) { - // Push the next min (left most) child node to the stack - case (?childNode) { - childIndex := 0; - currentNode := childNode; - Stack.push( - nodeCursorStack, - { - node = currentNode; - kvIndex = childIndex - } - ) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.traverseMinSubtreeIter(), null child node error") - } - } - } - } - } - }; - - func traverseMinSubtreeIterFrom(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor, compare : (K, K) -> Order.Order, key : K) { - var currentNode = nodeCursor.node; - - label l loop { - let (node, childrenOption) = switch (currentNode) { - case (#leaf(leafNode)) (leafNode, null); - case (#internal(internalNode)) (internalNode, ?internalNode.children) - }; - let (i, isFound) = switch (NodeUtil.getKeyIndex(node.data, compare, key)) { - case (#keyFound(i)) (i, true); - case (#notFound(i)) (i, false) - }; - if (i < node.data.count) { - Stack.push( - nodeCursorStack, - { - node = currentNode; - kvIndex = i // greater entries to traverse - } - ) - }; - if isFound return; - let ?children = childrenOption else return; - let ?childNode = children[i] else Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.traverseMinSubtreeIterFrom(), null child node error"); - currentNode := childNode - } - }; - - // traverse the max subtree of the current node cursor, passing each new element to the node cursor stack - func traverseMaxSubtreeIter(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor) { - var currentNode = nodeCursor.node; - var childIndex = nodeCursor.kvIndex; - - label l loop { - switch (currentNode) { - // If currentNode is leaf, have hit the maximum element of the subtree and already pushed it's cursor to the stack - // so can return - case (#leaf(_)) { - return - }; - // If currentNode is internal, add it's right most child to the stack and continue traversing - case (#internal(internalNode)) { - assert (childIndex <= internalNode.data.count); // children are one more than data entries - switch (internalNode.children[childIndex]) { - // Push the next max (right most) child node to the stack - case (?childNode) { - childIndex := switch (childNode) { - case (#internal(internalNode)) internalNode.data.count; - case (#leaf(leafNode)) leafNode.data.count - }; - currentNode := childNode; - Stack.push( - nodeCursorStack, - { - node = currentNode; - kvIndex = childIndex - } - ) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.traverseMaxSubtreeIter(), null child node error") - } - } - } - } - } - }; - - func traverseMaxSubtreeIterFrom(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor, compare : (K, K) -> Order.Order, key : K) { - var currentNode = nodeCursor.node; - - label l loop { - let (node, childrenOption) = switch (currentNode) { - case (#leaf(leafNode)) (leafNode, null); - case (#internal(internalNode)) (internalNode, ?internalNode.children) - }; - let (i, isFound) = switch (NodeUtil.getKeyIndex(node.data, compare, key)) { - case (#keyFound(i)) (i + 1, true); // +1 to include this key - case (#notFound(i)) (i, false) // i is the index of the first key less than the search key, or 0 if all keys are greater than the search key - }; - if (i > 0) { - Stack.push( - nodeCursorStack, - { - node = currentNode; - kvIndex = i - } - ) - }; - if isFound return; - let ?children = childrenOption else return; - let ?childNode = children[i] else Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.traverseMaxSubtreeIterFrom(), null child node error"); - currentNode := childNode - } - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateInternalDeleteResult = { - // element was deleted or not found, returning the old value (?value or null) - #delete : ?V; - // deleted an element, but was unable to successfully borrow and rebalance at the previous level without merging children - // the internalChild is the merged child that needs to be rebalanced at the next level up in the BTree - #mergeChild : { - internalChild : Internal; - deletedValue : ?V - } - }; - - func internalDeleteHelper(internalNode : Internal, order : Nat, compare : (K, K) -> Order.Order, deleteKey : K, skipNode : Bool) : IntermediateInternalDeleteResult { - let minKeys = NodeUtil.minKeysFromOrder(order); - let keyIndex = NodeUtil.getKeyIndex(internalNode.data, compare, deleteKey); - - // match on both the result of the node binary search, and if this node level should be skipped even if the key is found (internal kv replacement case) - switch (keyIndex, skipNode) { - // if key is found in the internal node - case (#keyFound(deleteIndex), false) { - let deletedValue = switch (internalNode.data.kvs[deleteIndex]) { - case (?kv) { ?kv.1 }; - case null { assert false; null } - }; - // TODO: (optimization) replace with deletion in one step without having to retrieve the maxKey first - let replaceKV = NodeUtil.getMaxKeyValue(internalNode.children[deleteIndex]); - internalNode.data.kvs[deleteIndex] := ?replaceKV; - switch (internalDeleteHelper(internalNode, order, compare, replaceKV.0, true)) { - case (#delete(_)) { #delete(deletedValue) }; - case (#mergeChild({ internalChild })) { - #mergeChild({ internalChild; deletedValue }) - } - } - }; - // if key is not found in the internal node OR the key is found, but skipping this node (because deleting the in order precessor i.e. replacement kv) - // in both cases need to descend and traverse to find the kv to delete - case ((#keyFound(_), true) or (#notFound(_), _)) { - let childIndex = switch (keyIndex) { - case (#keyFound(replacedSkipKeyIndex)) { replacedSkipKeyIndex }; - case (#notFound(childIndex)) { childIndex } - }; - let child = switch (internalNode.children[childIndex]) { - case (?c) { c }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalDeleteHelper, child index of #keyFound or #notfound is null") - } - }; - switch (child) { - // if child is internal - case (#internal(internalChild)) { - switch (internalDeleteHelper(internalChild, order, compare, deleteKey, false), childIndex == 0) { - // if value was successfully deleted and no additional tree re-balancing is needed, return the deleted value - case (#delete(v), _) { #delete(v) }; - // if internalChild needs rebalancing and pulling child is left most - case (#mergeChild({ internalChild; deletedValue }), true) { - // try to pull left-most key and child from right sibling - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex + 1, #successor)) { - // if can pull up sibling kv and child - case (#borrowed({ deletedSiblingKVPair; child })) { - NodeUtil.rotateBorrowedKVsAndChildFromSibling( - internalNode, - childIndex, - deletedSiblingKVPair, - child, - internalChild, - #right - ); - #delete(deletedValue) - }; - // unable to pull from sibling, need to merge with right sibling and push down parent - case (#notEnoughKeys(sibling)) { - // get the parent kv that will be pushed down the the child - let kvPairToBePushedToChild = ?BTreeHelper.deleteAndShift(internalNode.data.kvs, 0); - internalNode.data.count -= 1; - // merge the children and push down the parent - let newChild = NodeUtil.mergeChildrenAndPushDownParent(internalChild, kvPairToBePushedToChild, sibling); - // update children of the parent - internalNode.children[0] := ?#internal(newChild); - ignore ?BTreeHelper.deleteAndShift(internalNode.children, 1); - - if (internalNode.data.count < minKeys) { - #mergeChild({ internalChild = internalNode; deletedValue }) - } else { - #delete(deletedValue) - } - } - } - }; - // if internalChild needs rebalancing and pulling child is > 0, so a left sibling exists - case (#mergeChild({ internalChild; deletedValue }), false) { - // try to pull right-most key and its child directly from left sibling - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex - 1 : Nat, #predecessor)) { - case (#borrowed({ deletedSiblingKVPair; child })) { - NodeUtil.rotateBorrowedKVsAndChildFromSibling( - internalNode, - childIndex - 1 : Nat, - deletedSiblingKVPair, - child, - internalChild, - #left - ); - #delete(deletedValue) - }; - // unable to pull from left sibling - case (#notEnoughKeys(leftSibling)) { - // if child is not last index, try to pull from the right child - if (childIndex < internalNode.data.count) { - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex, #successor)) { - // if can pull up sibling kv and child - case (#borrowed({ deletedSiblingKVPair; child })) { - NodeUtil.rotateBorrowedKVsAndChildFromSibling( - internalNode, - childIndex, - deletedSiblingKVPair, - child, - internalChild, - #right - ); - return #delete(deletedValue) - }; - // if cannot borrow, from left or right, merge (see below) - case _ {} - } - }; - - // get the parent kv that will be pushed down the the child - let kvPairToBePushedToChild = ?BTreeHelper.deleteAndShift(internalNode.data.kvs, childIndex - 1 : Nat); - internalNode.data.count -= 1; - // merge it the children and push down the parent - let newChild = NodeUtil.mergeChildrenAndPushDownParent(leftSibling, kvPairToBePushedToChild, internalChild); - - // update children of the parent - internalNode.children[childIndex - 1] := ?#internal(newChild); - ignore ?BTreeHelper.deleteAndShift(internalNode.children, childIndex); - - if (internalNode.data.count < minKeys) { - #mergeChild({ internalChild = internalNode; deletedValue }) - } else { - #delete(deletedValue) - } - } - } - } - } - }; - // if child is leaf - case (#leaf(leafChild)) { - switch (leafDeleteHelper(leafChild, order, compare, deleteKey), childIndex == 0) { - case (#delete(value), _) { #delete(value) }; - // if delete child is left most, try to borrow from right child - case (#mergeLeafData({ leafDeleteIndex }), true) { - switch (NodeUtil.borrowFromRightLeafChild(internalNode.children, childIndex)) { - case (?borrowedKVPair) { - let kvPairToBePushedToChild = internalNode.data.kvs[childIndex]; - internalNode.data.kvs[childIndex] := ?borrowedKVPair; - - let deletedKV = BTreeHelper.insertAtPostionAndDeleteAtPosition<(K, V)>(leafChild.data.kvs, kvPairToBePushedToChild, leafChild.data.count - 1, leafDeleteIndex); - #delete(?deletedKV.1) - }; - - case null { - // can't borrow from right child, delete from leaf and merge with right child and parent kv, then push down into new leaf - let rightChild = switch (internalNode.children[childIndex + 1]) { - case (?#leaf(rc)) { rc }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalDeleteHelper, if trying to borrow from right leaf child is null, rightChild index cannot be null or internal") - } - }; - let (mergedLeaf, deletedKV) = mergeParentWithLeftRightChildLeafNodesAndDelete( - internalNode.data.kvs[childIndex], - leafChild, - rightChild, - leafDeleteIndex, - #left - ); - // delete the left most internal node kv, since was merging from a deletion in left most child (0) and the parent kv was pushed into the mergedLeaf - ignore BTreeHelper.deleteAndShift<(K, V)>(internalNode.data.kvs, 0); - // update internal node children - BTreeHelper.replaceTwoWithElementAndShift>(internalNode.children, #leaf(mergedLeaf), 0); - internalNode.data.count -= 1; - - if (internalNode.data.count < minKeys) { - #mergeChild({ - internalChild = internalNode; - deletedValue = ?deletedKV.1 - }) - } else { - #delete(?deletedKV.1) - } - - } - } - }; - // if delete child is middle or right most, try to borrow from left child - case (#mergeLeafData({ leafDeleteIndex }), false) { - // if delete child is right most, try to borrow from left child - switch (NodeUtil.borrowFromLeftLeafChild(internalNode.children, childIndex)) { - case (?borrowedKVPair) { - let kvPairToBePushedToChild = internalNode.data.kvs[childIndex - 1]; - internalNode.data.kvs[childIndex - 1] := ?borrowedKVPair; - let kvDelete = BTreeHelper.insertAtPostionAndDeleteAtPosition<(K, V)>(leafChild.data.kvs, kvPairToBePushedToChild, 0, leafDeleteIndex); - #delete(?kvDelete.1) - }; - case null { - // if delete child is in the middle, try to borrow from right child - if (childIndex < internalNode.data.count) { - // try to borrow from right - switch (NodeUtil.borrowFromRightLeafChild(internalNode.children, childIndex)) { - case (?borrowedKVPair) { - let kvPairToBePushedToChild = internalNode.data.kvs[childIndex]; - internalNode.data.kvs[childIndex] := ?borrowedKVPair; - // insert the successor at the very last element - let kvDelete = BTreeHelper.insertAtPostionAndDeleteAtPosition<(K, V)>(leafChild.data.kvs, kvPairToBePushedToChild, leafChild.data.count - 1, leafDeleteIndex); - return #delete(?kvDelete.1) - }; - // if cannot borrow, from left or right, merge (see below) - case _ {} - } - }; - - // can't borrow from left child, delete from leaf and merge with left child and parent kv, then push down into new leaf - let leftChild = switch (internalNode.children[childIndex - 1]) { - case (?#leaf(lc)) { lc }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalDeleteHelper, if trying to borrow from left leaf child is null, then left child index must not be null or internal") - } - }; - let (mergedLeaf, deletedKV) = mergeParentWithLeftRightChildLeafNodesAndDelete( - internalNode.data.kvs[childIndex - 1], - leftChild, - leafChild, - leafDeleteIndex, - #right - ); - // delete the right most internal node kv, since was merging from a deletion in the right most child and the parent kv was pushed into the mergedLeaf - ignore BTreeHelper.deleteAndShift<(K, V)>(internalNode.data.kvs, childIndex - 1); - // update internal node children - BTreeHelper.replaceTwoWithElementAndShift>(internalNode.children, #leaf(mergedLeaf), childIndex - 1); - internalNode.data.count -= 1; - - if (internalNode.data.count < minKeys) { - #mergeChild({ - internalChild = internalNode; - deletedValue = ?deletedKV.1 - }) - } else { - #delete(?deletedKV.1) - } - } - } - } - } - } - } - } - } - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateLeafDeleteResult = { - // element was deleted or not found, returning the old value (?value or null) - #delete : ?V; - // leaf had the minimum number of keys when deleting, so returns the leaf node's data and the index of the key that will be deleted - #mergeLeafData : { - data : Data; - leafDeleteIndex : Nat - } - }; - - func leafDeleteHelper(leafNode : Leaf, order : Nat, compare : (K, K) -> Order.Order, deleteKey : K) : IntermediateLeafDeleteResult { - let minKeys = NodeUtil.minKeysFromOrder(order); - - switch (NodeUtil.getKeyIndex(leafNode.data, compare, deleteKey)) { - case (#keyFound(deleteIndex)) { - if (leafNode.data.count > minKeys) { - leafNode.data.count -= 1; - #delete(?BTreeHelper.deleteAndShift<(K, V)>(leafNode.data.kvs, deleteIndex).1) - } else { - #mergeLeafData({ - data = leafNode.data; - leafDeleteIndex = deleteIndex - }) - } - }; - case (#notFound(_)) { - #delete(null) - } - } - }; - - // get helper if internal node - func getFromInternal(internalNode : Internal, compare : (K, K) -> Order.Order, key : K) : ?V { - switch (NodeUtil.getKeyIndex(internalNode.data, compare, key)) { - case (#keyFound(index)) { - getExistingValueFromIndex(internalNode.data, index) - }; - case (#notFound(index)) { - switch (internalNode.children[index]) { - // expects the child to be there, otherwise there's a bug in binary search or the tree is invalid - case null { Runtime.trap("Internal bug: Map.getFromInternal") }; - case (?#leaf(leafNode)) { getFromLeaf(leafNode, compare, key) }; - case (?#internal(internalNode)) { - getFromInternal(internalNode, compare, key) - } - } - } - } - }; - - // get function helper if leaf node - func getFromLeaf(leafNode : Leaf, compare : (K, K) -> Order.Order, key : K) : ?V { - switch (NodeUtil.getKeyIndex(leafNode.data, compare, key)) { - case (#keyFound(index)) { - getExistingValueFromIndex(leafNode.data, index) - }; - case _ null - } - }; - - // get function helper that retrieves an existing value in the case that the key is found - func getExistingValueFromIndex(data : Data, index : Nat) : ?V { - switch (data.kvs[index]) { - case null { null }; - case (?ov) { ?ov.1 } - } - }; - - // which child the deletionIndex is referring to - type DeletionSide = { #left; #right }; - - func mergeParentWithLeftRightChildLeafNodesAndDelete( - parentKV : ?(K, V), - leftChild : Leaf, - rightChild : Leaf, - deleteIndex : Nat, - deletionSide : DeletionSide - ) : (Leaf, (K, V)) { - let count = leftChild.data.count * 2; - let (kvs, deletedKV) = BTreeHelper.mergeParentWithChildrenAndDelete<(K, V)>( - parentKV, - leftChild.data.count, - leftChild.data.kvs, - rightChild.data.kvs, - deleteIndex, - deletionSide - ); - ( - { - data = { - kvs; - var count = count - } - }, - deletedKV - ) - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateInsertResult = { - // element was inserted or replaced, returning the old value (?value or null) - #insert : ?V; - // child was full when inserting, so returns the promoted kv pair and the split left and right child - #promote : { - kv : (K, V); - leftChild : Node; - rightChild : Node - } - }; - - // Helper for inserting into a leaf node - func leafInsertHelper(leafNode : Leaf, order : Nat, compare : (K, K) -> Order.Order, key : K, value : V) : (IntermediateInsertResult) { - // Perform binary search to see if the element exists in the node - switch (NodeUtil.getKeyIndex(leafNode.data, compare, key)) { - case (#keyFound(insertIndex)) { - let previous = leafNode.data.kvs[insertIndex]; - leafNode.data.kvs[insertIndex] := ?(key, value); - switch (previous) { - case (?ov) { #insert(?ov.1) }; - case null { assert false; #insert(null) }; // the binary search already found an element, so this case should never happen - } - }; - case (#notFound(insertIndex)) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - let maxKeys : Nat = order - 1; - // If the leaf is full, insert, split the node, and promote the middle element - if (leafNode.data.count >= maxKeys) { - let (leftKVs, promotedParentElement, rightKVs) = BTreeHelper.insertOneAtIndexAndSplitArray( - leafNode.data.kvs, - (key, value), - insertIndex - ); - - let leftCount = order / 2; - let rightCount : Nat = if (order % 2 == 0) { leftCount - 1 } else { - leftCount - }; - - ( - #promote({ - kv = promotedParentElement; - leftChild = createLeaf(leftKVs, leftCount); - rightChild = createLeaf(rightKVs, rightCount) - }) - ) - } - // Otherwise, insert at the specified index (shifting elements over if necessary) - else { - NodeUtil.insertAtIndexOfNonFullNodeData(leafNode.data, ?(key, value), insertIndex); - #insert(null) - } - } - } - }; - - // Helper for inserting into an internal node - func internalInsertHelper(internalNode : Internal, order : Nat, compare : (K, K) -> Order.Order, key : K, value : V) : IntermediateInsertResult { - switch (NodeUtil.getKeyIndex(internalNode.data, compare, key)) { - case (#keyFound(insertIndex)) { - let previous = internalNode.data.kvs[insertIndex]; - internalNode.data.kvs[insertIndex] := ?(key, value); - switch (previous) { - case (?ov) { #insert(?ov.1) }; - case null { assert false; #insert(null) }; // the binary search already found an element, so this case should never happen - } - }; - case (#notFound(insertIndex)) { - let insertResult = switch (internalNode.children[insertIndex]) { - case null { assert false; #insert(null) }; - case (?#leaf(leafNode)) { - leafInsertHelper(leafNode, order, compare, key, value) - }; - case (?#internal(internalChildNode)) { - internalInsertHelper(internalChildNode, order, compare, key, value) - } - }; - - switch (insertResult) { - case (#insert(ov)) { #insert(ov) }; - case (#promote({ kv; leftChild; rightChild })) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - let maxKeys : Nat = order - 1; - // if current internal node is full, need to split the internal node - if (internalNode.data.count >= maxKeys) { - // insert and split internal kvs, determine new promotion target kv - let (leftKVs, promotedParentElement, rightKVs) = BTreeHelper.insertOneAtIndexAndSplitArray( - internalNode.data.kvs, - (kv), - insertIndex - ); - - // calculate the element count in the left KVs and the element count in the right KVs - let leftCount = order / 2; - let rightCount : Nat = if (order % 2 == 0) { leftCount - 1 } else { - leftCount - }; - - // split internal children - let (leftChildren, rightChildren) = NodeUtil.splitChildrenInTwoWithRebalances( - internalNode.children, - insertIndex, - leftChild, - rightChild - ); - - // send the kv to be promoted, as well as the internal children left and right split - #promote({ - kv = promotedParentElement; - leftChild = #internal({ - data = { kvs = leftKVs; var count = leftCount }; - children = leftChildren - }); - rightChild = #internal({ - data = { kvs = rightKVs; var count = rightCount }; - children = rightChildren - }) - }) - } else { - // insert the new kvs into the internal node - NodeUtil.insertAtIndexOfNonFullNodeData(internalNode.data, ?kv, insertIndex); - // split and re-insert the single child that needs rebalancing - NodeUtil.insertRebalancedChild(internalNode.children, insertIndex, leftChild, rightChild); - #insert(null) - } - } - } - } - } - }; - - func createLeaf(kvs : [var ?(K, V)], count : Nat) : Node { - #leaf({ - data = { - kvs; - var count - } - }) - }; - - // Additional functionality compared to original source. - - func mapData(data : Data, project : (K, V1) -> V2) : Data { - { - kvs = VarArray.map( - data.kvs, - func entry { - switch entry { - case (?kv) ?(kv.0, project kv); - case null null - } - } - ); - var count = data.count - } - }; - - func mapNode(node : Node, project : (K, V1) -> V2) : Node { - switch node { - case (#leaf { data }) { - #leaf { data = mapData(data, project) } - }; - case (#internal { data; children }) { - let mappedData = mapData(data, project); - let mappedChildren = VarArray.map, ?Node>( - children, - func child { - switch child { - case null null; - case (?childNode) ?mapNode(childNode, project) - } - } - ); - # internal({ - data = mappedData; - children = mappedChildren - }) - } - } - }; - - func cloneNode(node : Node) : Node = mapNode(node, func(k, v) = v); - - module BinarySearch { - public type SearchResult = { - #keyFound : Nat; - #notFound : Nat - }; - - /// Searches an array for a specific key, returning the index it occurs at if #keyFound, or the child/insert index it may occur at - /// if #notFound. This is used when determining if a key exists in an internal or leaf node, where a key should be inserted in a - /// leaf node, or which child of an internal node a key could be in. - /// - /// Note: This function expects a mutable, nullable, array of keys in sorted order, where all nulls appear at the end of the array. - /// This function may trap if a null value appears before any values. It also expects a maxIndex, which is the right-most index (bound) - /// from which to begin the binary search (the left most bound is expected to be 0) - /// - /// Parameters: - /// - /// * array - the sorted array that the binary search is performed upon - /// * compare - the comparator used to perform the search - /// * searchKey - the key being compared against in the search - /// * maxIndex - the right-most index (bound) from which to begin the search - public func binarySearchNode(array : [var ?(K, V)], compare : (implicit : (K, K) -> Order.Order), searchKey : K, maxIndex : Nat) : SearchResult { - // TODO: get rid of this check? - // Trap if array is size 0 (should not happen) - if (array.size() == 0) { - assert false - }; - - // if all elements in the array are null (i.e. first element is null), return #notFound(0) - if (maxIndex == 0) { - return #notFound(0) - }; - - // Initialize search from first to last index - var left : Nat = 0; - var right = maxIndex; // maxIndex does not necessarily mean array.size() - 1 - // Search the array - while (left < right) { - let middle = (left + right) / 2; - switch (array[middle]) { - case null { assert false }; - case (?(key, _)) { - switch (compare(searchKey, key)) { - // If the element is present at the middle itself - case (#equal) { return #keyFound(middle) }; - // If element is greater than mid, it can only be present in left subarray - case (#greater) { left := middle + 1 }; - // If element is smaller than mid, it can only be present in right subarray - case (#less) { - right := if (middle == 0) { 0 } else { middle - 1 } - } - } - } - } - }; - - if (left == array.size()) { - return #notFound(left) - }; - - // left == right - switch (array[left]) { - // inserting at end of array - case null { #notFound(left) }; - case (?(key, _)) { - switch (compare(searchKey, key)) { - // if left is the key - case (#equal) { #keyFound(left) }; - // if the key is not found, return notFound and the insert location - case (#greater) { #notFound(left + 1) }; - case (#less) { #notFound(left) } - } - } - } - } - }; - - module NodeUtil { - /// Inserts element at the given index into a non-full leaf node - public func insertAtIndexOfNonFullNodeData(data : Data, kvPair : ?(K, V), insertIndex : Nat) { - let currentLastElementIndex : Nat = if (data.count == 0) { 0 } else { - data.count - 1 - }; - BTreeHelper.insertAtPosition<(K, V)>(data.kvs, kvPair, insertIndex, currentLastElementIndex); - - // increment the count of data in this node since just inserted an element - data.count += 1 - }; - - /// Inserts two rebalanced (split) child halves into a non-full array of children. - public func insertRebalancedChild(children : [var ?Node], rebalancedChildIndex : Nat, leftChildInsert : Node, rightChildInsert : Node) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - var j : Nat = children.size() - 2; - - // This is just a sanity check to ensure the children aren't already full (should split promote otherwise) - // TODO: Remove this check once confident - if (Option.isSome(children[j + 1])) { assert false }; - - // Iterate backwards over the array and shift each element over to the right by one until the rebalancedChildIndex is hit - while (j > rebalancedChildIndex) { - children[j + 1] := children[j]; - j -= 1 - }; - - // Insert both the left and right rebalanced children (replacing the pre-split child) - children[j] := ?leftChildInsert; - children[j + 1] := ?rightChildInsert - }; - - /// Used when splitting the children of an internal node - /// - /// Takes in the rebalanced child index, as well as both halves of the rebalanced child and splits the children, inserting the left and right child halves appropriately - /// - /// For more context, see the documentation for the splitArrayAndInsertTwo method in BTreeHelper.mo - public func splitChildrenInTwoWithRebalances( - children : [var ?Node], - rebalancedChildIndex : Nat, - leftChildInsert : Node, - rightChildInsert : Node - ) : ([var ?Node], [var ?Node]) { - BTreeHelper.splitArrayAndInsertTwo>(children, rebalancedChildIndex, leftChildInsert, rightChildInsert) - }; - - /// Helper used to get the key index of of a key within a node - /// - /// for more, see the BinarySearch.binarySearchNode() documentation - public func getKeyIndex(data : Data, compare : (K, K) -> Order.Order, key : K) : BinarySearch.SearchResult { - BinarySearch.binarySearchNode(data.kvs, compare, key, data.count) - }; - - // calculates a BTree Node's minimum allowed keys given the order of the BTree - public func minKeysFromOrder(order : Nat) : Nat { - if (order % 2 == 0) { order / 2 - 1 } else { order / 2 } - }; - - // Given a node, get the maximum key value (right most leaf kv) - public func getMaxKeyValue(node : ?Node) : (K, V) { - switch (node) { - case (?#leaf({ data })) { - switch (data.kvs[data.count - 1]) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.NodeUtil.getMaxKeyValue, data cannot have more elements than it's count") - }; - case (?kv) { kv } - } - }; - case (?#internal({ data; children })) { - getMaxKeyValue(children[data.count]) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.NodeUtil.getMaxKeyValue, the node provided cannot be null") - } - } - }; - - type InorderBorrowType = { - #predecessor; - #successor - }; - - // attempts to retrieve the in max key of the child leaf node directly to the left if the node will allow it - // returns the deleted max key if able to retrieve, null if not able - // - // mutates the predecessing node's keys - public func borrowFromLeftLeafChild(children : [var ?Node], ofChildIndex : Nat) : ?(K, V) { - let predecessorIndex : Nat = ofChildIndex - 1; - borrowFromLeafChild(children, predecessorIndex, #predecessor) - }; - - // attempts to retrieve the in max key of the child leaf node directly to the right if the node will allow it - // returns the deleted max key if able to retrieve, null if not able - // - // mutates the predecessing node's keys - public func borrowFromRightLeafChild(children : [var ?Node], ofChildIndex : Nat) : ?(K, V) { - borrowFromLeafChild(children, ofChildIndex + 1, #successor) - }; - - func borrowFromLeafChild(children : [var ?Node], borrowChildIndex : Nat, childSide : InorderBorrowType) : ?(K, V) { - let minKeys = minKeysFromOrder(children.size()); - - switch (children[borrowChildIndex]) { - case (?#leaf({ data })) { - if (data.count > minKeys) { - // able to borrow a key-value from this child, so decrement the count of kvs - data.count -= 1; // Since enforce order >= 4, there will always be at least 1 element per node - switch (childSide) { - case (#predecessor) { - let deletedKV = data.kvs[data.count]; - data.kvs[data.count] := null; - deletedKV - }; - case (#successor) { - ?BTreeHelper.deleteAndShift(data.kvs, 0) - } - } - } else { null } - }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.NodeUtil.borrowFromLeafChild, the node at the borrow child index cannot be null or internal") - } - } - }; - - type InternalBorrowResult = { - #borrowed : InternalBorrow; - #notEnoughKeys : Internal - }; - - type InternalBorrow = { - deletedSiblingKVPair : ?(K, V); - child : ?Node - }; - - // Attempts to borrow a KV and child from an internal sibling node - public func borrowFromInternalSibling(children : [var ?Node], borrowChildIndex : Nat, borrowType : InorderBorrowType) : InternalBorrowResult { - let minKeys = minKeysFromOrder(children.size()); - - switch (children[borrowChildIndex]) { - case (?#internal({ data; children })) { - if (data.count > minKeys) { - data.count -= 1; - switch (borrowType) { - case (#predecessor) { - let deletedSiblingKVPair = data.kvs[data.count]; - data.kvs[data.count] := null; - let child = children[data.count + 1]; - children[data.count + 1] := null; - #borrowed({ - deletedSiblingKVPair; - child - }) - }; - case (#successor) { - #borrowed({ - deletedSiblingKVPair = ?BTreeHelper.deleteAndShift(data.kvs, 0); - child = ?BTreeHelper.deleteAndShift(children, 0) - }) - } - } - } else { #notEnoughKeys({ data; children }) } - }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.NodeUtil.borrowFromInternalSibling from internal sibling, the child at the borrow index cannot be null or a leaf") - } - } - }; - - type SiblingSide = { #left; #right }; - - // Rotates the borrowed KV and child from sibling side of the internal node to the internal child recipient - public func rotateBorrowedKVsAndChildFromSibling( - internalNode : Internal, - parentRotateIndex : Nat, - borrowedSiblingKVPair : ?(K, V), - borrowedSiblingChild : ?Node, - internalChildRecipient : Internal, - siblingSide : SiblingSide - ) { - // if borrowing from the left, the rotated key and child will always be inserted first - // if borrowing from the right, the rotated key and child will always be inserted last - let (kvIndex, childIndex) = switch (siblingSide) { - case (#left) { (0, 0) }; - case (#right) { - (internalChildRecipient.data.count, internalChildRecipient.data.count + 1) - } - }; - - // get the parent kv that will be pushed down the the child - let kvPairToBePushedToChild = internalNode.data.kvs[parentRotateIndex]; - // replace the parent with the sibling kv - internalNode.data.kvs[parentRotateIndex] := borrowedSiblingKVPair; - // push the kv and child down into the internalChild - insertAtIndexOfNonFullNodeData(internalChildRecipient.data, kvPairToBePushedToChild, kvIndex); - - BTreeHelper.insertAtPosition>(internalChildRecipient.children, borrowedSiblingChild, childIndex, internalChildRecipient.data.count) - }; - - // Merges the kvs and children of two internal nodes, pushing the parent kv in between the right and left halves - public func mergeChildrenAndPushDownParent(leftChild : Internal, parentKV : ?(K, V), rightChild : Internal) : Internal { - { - data = mergeData(leftChild.data, parentKV, rightChild.data); - children = mergeChildren(leftChild.children, rightChild.children) - } - }; - - func mergeData(leftData : Data, parentKV : ?(K, V), rightData : Data) : Data { - assert leftData.count <= minKeysFromOrder(leftData.kvs.size() + 1); - assert rightData.count <= minKeysFromOrder(rightData.kvs.size() + 1); - - let mergedKVs = VarArray.repeat(null, leftData.kvs.size()); - var i = 0; - while (i < leftData.count) { - mergedKVs[i] := leftData.kvs[i]; - i += 1 - }; - - mergedKVs[i] := parentKV; - i += 1; - - var j = 0; - while (j < rightData.count) { - mergedKVs[i] := rightData.kvs[j]; - i += 1; - j += 1 - }; - - { - kvs = mergedKVs; - var count = leftData.count + 1 + rightData.count - } - }; - - func mergeChildren(leftChildren : [var ?Node], rightChildren : [var ?Node]) : [var ?Node] { - let mergedChildren = VarArray.repeat>(null, leftChildren.size()); - var i = 0; - - while (Option.isSome(leftChildren[i])) { - mergedChildren[i] := leftChildren[i]; - i += 1 - }; - - var j = 0; - while (Option.isSome(rightChildren[j])) { - mergedChildren[i] := rightChildren[j]; - i += 1; - j += 1 - }; - - mergedChildren - } - } -} diff --git a/.mops/core@2.4.0/src/Nat.mo b/.mops/core@2.4.0/src/Nat.mo deleted file mode 100644 index e93f58a..0000000 --- a/.mops/core@2.4.0/src/Nat.mo +++ /dev/null @@ -1,671 +0,0 @@ -/// Natural numbers with infinite precision. -/// -/// Most operations on natural numbers (e.g. addition) are available as built-in operators (e.g. `1 + 1`). -/// This module provides equivalent functions and `Text` conversion. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat "mo:core/Nat"; -/// ``` - -import Int "Int"; -import Prim "mo:⛔"; -import Char "Char"; -import Iter "Iter"; -import Runtime "Runtime"; -import Order "Order"; - -module { - - /// Infinite precision natural numbers. - public type Nat = Prim.Types.Nat; - - /// Converts a natural number to its textual representation. Textual - /// representation _do not_ contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toText(1234) == "1234"; - /// ``` - public let toText : (self : Nat) -> Text = Int.toText; - - /// Creates a natural number from its textual representation. Returns `null` - /// if the input is not a valid natural number. - /// - /// The textual representation _must not_ contain underscores. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromText("1234") == ?1234; - /// ``` - public func fromText(text : Text) : ?Nat { - if (text == "") { - return null - }; - var n = 0; - for (c in text.chars()) { - if (Char.isDigit(c)) { - let charAsNat = Prim.nat32ToNat(Prim.charToNat32(c) -% Prim.charToNat32('0')); - n := n * 10 + charAsNat - } else { - return null - } - }; - ?n - }; - - /// Creates a natural number from its textual representation. Returns `null` - /// if the input is not a valid natural number. - /// - /// The textual representation _must not_ contain underscores. - /// - /// This functions is meant to be used with contextual-dot notation. - /// - /// Example: - /// ```motoko include=import - /// assert "1234".toNat() == ?1234; - /// ``` - public let toNat : (self : Text) -> ?Nat = fromText; - - /// Converts an integer to a natural number. Traps if the integer is negative. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromInt(1234) == (1234 : Nat); - /// ``` - /// @deprecated M0235 - public func fromInt(int : Int) : Nat { - if (int < 0) { - Runtime.trap("Nat.fromInt(): negative input value") - } else { - Int.abs(int) - } - }; - - /// Conversion to Float. May result in `Inf`. - /// - /// Note: The floating point number may be imprecise for large Nat values. - /// Returns `inf` if the integer is greater than the maximum floating point number. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toFloat(123) == 123.0; - /// ``` - public let toFloat : (self : Nat) -> Float = Int.toFloat; - - /// Converts a natural number to an integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toInt(1234) == 1234; - /// ``` - public func toInt(self : Nat) : Int { - self : Int - }; - - /// Converts an unsigned integer with infinite precision to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toNat8(123) == (123 : Nat8); - /// ``` - public let toNat8 : (self : Nat) -> Nat8 = Prim.natToNat8; - - /// Converts an unsigned integer with infinite precision to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toNat16(123) == (123 : Nat16); - /// ``` - public let toNat16 : (self : Nat) -> Nat16 = Prim.natToNat16; - - /// Converts an unsigned integer with infinite precision to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toNat32(123) == (123 : Nat32); - /// ``` - public let toNat32 : (self : Nat) -> Nat32 = Prim.natToNat32; - - /// Converts an unsigned integer with infinite precision to a 64-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toNat64(123) == (123 : Nat64); - /// ``` - public let toNat64 : (self : Nat) -> Nat64 = Prim.natToNat64; - - /// Converts an 8-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromNat8(123) == (123 : Nat); - /// ``` - public let fromNat8 : Nat8 -> Nat = Prim.nat8ToNat; - - /// Converts a 16-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromNat16(123) == (123 : Nat); - /// ``` - public let fromNat16 : Nat16 -> Nat = Prim.nat16ToNat; - - /// Converts a 32-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromNat32(123) == (123 : Nat); - /// ``` - public let fromNat32 : Nat32 -> Nat = Prim.nat32ToNat; - - /// Converts a 64-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromNat64(123) == (123 : Nat); - /// ``` - public let fromNat64 : Nat64 -> Nat = Prim.nat64ToNat; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.min(1, 2) == 1; - /// ``` - public func min(x : Nat, y : Nat) : Nat { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.max(1, 2) == 2; - /// ``` - public func max(x : Nat, y : Nat) : Nat { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.equal(1, 1); - /// assert 1 == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a = 111; - /// let b = 222; - /// assert not Nat.equal(a, b); - /// ``` - public func equal(x : Nat, y : Nat) : Bool { x == y }; - - /// Inequality function for Nat types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.notEqual(1, 2); - /// assert 1 != 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat, y : Nat) : Bool { x != y }; - - /// "Less than" function for Nat types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.less(1, 2); - /// assert 1 < 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat, y : Nat) : Bool { x < y }; - - /// "Less than or equal" function for Nat types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.lessOrEqual(1, 2); - /// assert 1 <= 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat, y : Nat) : Bool { x <= y }; - - /// "Greater than" function for Nat types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.greater(2, 1); - /// assert 2 > 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat, y : Nat) : Bool { x > y }; - - /// "Greater than or equal" function for Nat types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.greaterOrEqual(2, 1); - /// assert 2 >= 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat, y : Nat) : Bool { x >= y }; - - /// General purpose comparison function for `Nat`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1], Nat.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat, y : Nat) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. This operator will never overflow - /// because `Nat` is infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.add(1, 2) == 3; - /// assert 1 + 2 == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat.add) == 6; - /// ``` - public func add(x : Nat, y : Nat) : Nat { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow below `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.sub(2, 1) == 1; - /// // Add a type annotation to avoid a warning about the subtraction - /// assert 2 - 1 : Nat == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 10, Nat.sub) == 4; - /// ``` - public func sub(x : Nat, y : Nat) : Nat { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. This operator will never - /// overflow because `Nat` is infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.mul(2, 3) == 6; - /// assert 2 * 3 == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat.mul) == 6; - /// ``` - public func mul(x : Nat, y : Nat) : Nat { x * y }; - - /// Returns the unsigned integer division of `x` by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// The quotient is rounded down, which is equivalent to truncating the - /// decimal places of the quotient. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.div(6, 2) == 3; - /// assert 6 / 2 == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat, y : Nat) : Nat { x / y }; - - /// Returns the remainder of unsigned integer division of `x` by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.rem(6, 4) == 2; - /// assert 6 % 4 == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat, y : Nat) : Nat { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. Traps when `y > 2^32`. This operator - /// will never overflow because `Nat` is infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.pow(2, 3) == 8; - /// assert 2 ** 3 == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat, y : Nat) : Nat { x ** y }; - - /// Returns the (conceptual) bitwise shift left of `x` by `y`, `x * (2 ** y)`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.bitshiftLeft(1, 3) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in absence - /// of the `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. While `Nat` is not defined in terms - /// of bit patterns, conceptually it can be regarded as such, and the operation - /// is provided as a high-performance version of the corresponding arithmetic - /// rule. - public let bitshiftLeft : (x : Nat, y : Nat32) -> Nat = Prim.shiftLeft; - - /// Returns the (conceptual) bitwise shift right of `x` by `y`, `x / (2 ** y)`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.bitshiftRight(8, 3) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in absence - /// of the `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. While `Nat` is not defined in terms - /// of bit patterns, conceptually it can be regarded as such, and the operation - /// is provided as a high-performance version of the corresponding arithmetic - /// rule. - public let bitshiftRight : (x : Nat, y : Nat32) -> Nat = Prim.shiftRight; - - /// Returns an iterator over `Nat` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat, toExclusive : Nat) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat { - if (n >= toExclusive) { - return null - }; - let current = n; - n += 1; - ?current - } - } - } - }; - - /// Returns an iterator over `Nat` values from the first to second argument with an exclusive upper bound, - /// incrementing by the specified step size. The step can be positive or negative. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// // Positive step - /// let iter1 = Nat.rangeBy(1, 7, 2); - /// assert iter1.next() == ?1; - /// assert iter1.next() == ?3; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// // Negative step - /// let iter2 = Nat.rangeBy(7, 1, -2); - /// assert iter2.next() == ?7; - /// assert iter2.next() == ?5; - /// assert iter2.next() == ?3; - /// assert iter2.next() == null; - /// ``` - /// - /// If `step` is 0 or if the iteration would not progress towards the bound, returns an empty iterator. - public func rangeBy(fromInclusive : Nat, toExclusive : Nat, step : Int) : Iter.Iter { - if (step == 0 or (step > 0 and fromInclusive >= toExclusive) or (step < 0 and fromInclusive <= toExclusive)) { - Iter.empty() - } else if (step > 0) { - object { - let stepMagnitude = Int.abs(step); - var n = fromInclusive; - public func next() : ?Nat { - if (n >= toExclusive) { - return null - }; - let current = n; - n += stepMagnitude; - ?current - } - } - } else { - object { - let stepMagnitude = Int.abs(step); - var n = fromInclusive; - public func next() : ?Nat { - if (n <= toExclusive) { - return null - }; - let current = n; - if (stepMagnitude > n) { - n := 0 - } else { - n -= stepMagnitude - }; - ?current - } - } - } - }; - - /// Returns an iterator over the integers from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.rangeInclusive(3, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat, to : Nat) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - public func next() : ?Nat { - if (n > to) { - return null - }; - let current = n; - n += 1; - ?current - } - } - } - }; - - /// Returns an iterator over the integers from the first to second argument, inclusive, - /// incrementing by the specified step size. The step can be positive or negative. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// // Positive step - /// let iter1 = Nat.rangeByInclusive(1, 7, 2); - /// assert iter1.next() == ?1; - /// assert iter1.next() == ?3; - /// assert iter1.next() == ?5; - /// assert iter1.next() == ?7; - /// assert iter1.next() == null; - /// - /// // Negative step - /// let iter2 = Nat.rangeByInclusive(7, 1, -2); - /// assert iter2.next() == ?7; - /// assert iter2.next() == ?5; - /// assert iter2.next() == ?3; - /// assert iter2.next() == ?1; - /// assert iter2.next() == null; - /// ``` - /// - /// If `from == to`, return an iterator which only returns that value. - /// - /// Otherwise, if `step` is 0 or if the iteration would not progress towards the bound, returns an empty iterator. - public func rangeByInclusive(from : Nat, to : Nat, step : Int) : Iter.Iter { - if (from == to) { - Iter.singleton(from) - } else if (step == 0 or (step > 0 and from > to) or (step < 0 and from < to)) { - Iter.empty() - } else if (step > 0) { - object { - let stepMagnitude = Int.abs(step); - var n = from; - public func next() : ?Nat { - if (n > to) { - return null - }; - let current = n; - n += stepMagnitude; - ?current - } - } - } else { - object { - let stepMagnitude = Int.abs(step); - var n = from; - var done = false; - public func next() : ?Nat { - if (done) { - null - } else { - let current = n; - if (n < to + stepMagnitude) { - done := true - } else { - n -= stepMagnitude - }; - ?current - } - } - } - } - }; - - /// Returns an infinite iterator over all possible `Nat` values. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter = object { - var n = 0; - public func next() : ?Nat { - let current = n; - n += 1; - ?current - } - }; - -} diff --git a/.mops/core@2.4.0/src/Nat16.mo b/.mops/core@2.4.0/src/Nat16.mo deleted file mode 100644 index 4b1195d..0000000 --- a/.mops/core@2.4.0/src/Nat16.mo +++ /dev/null @@ -1,705 +0,0 @@ -/// Utility functions on 16-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat16 "mo:core/Nat16"; -/// ``` -import Nat "Nat"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 16-bit natural numbers. - public type Nat16 = Prim.Types.Nat16; - - /// Maximum 16-bit natural number. `2 ** 16 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.maxValue == (65535 : Nat16); - /// ``` - public let maxValue : Nat16 = 65535; - - /// Converts a 16-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toNat(123) == (123 : Nat); - /// ``` - public let toNat : (self : Nat16) -> Nat = Prim.nat16ToNat; - - /// Converts an unsigned integer with infinite precision to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromNat(123) == (123 : Nat16); - /// ``` - public let fromNat : Nat -> Nat16 = Prim.natToNat16; - - /// Converts an 8-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromNat8(123) == (123 : Nat16); - /// ``` - /// @deprecated M0235 - public let fromNat8 : (x : Nat8) -> Nat16 = Prim.nat8ToNat16; - - /// Converts a 16-bit unsigned integer to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toNat8(123) == (123 : Nat8); - /// ``` - public let toNat8 : (self : Nat16) -> Nat8 = Prim.nat16ToNat8; - - /// Converts a 32-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromNat32(123) == (123 : Nat16); - /// ``` - /// @deprecated M0235 - public let fromNat32 : (x : Nat32) -> Nat16 = Prim.nat32ToNat16; - - /// Converts a 16-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toNat32(123) == (123 : Nat32); - /// ``` - public let toNat32 : (self : Nat16) -> Nat32 = Prim.nat16ToNat32; - - /// Converts a 64-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromNat64(123) == (123 : Nat16); - /// ``` - /// @deprecated M0235 - public func fromNat64(x : Nat64) : Nat16 { - Prim.nat32ToNat16(Prim.nat64ToNat32(x)) - }; - - /// Converts a 16-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toNat64(123) == (123 : Nat64); - /// ``` - public func toNat64(self : Nat16) : Nat64 { - Prim.nat32ToNat64(Prim.nat16ToNat32(self)) - }; - - /// Converts a signed integer with infinite precision to a 16-bit unsigned integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromIntWrap(123 : Int) == (123 : Nat16); - /// ``` - public let fromIntWrap : Int -> Nat16 = Prim.intToNat16Wrap; - - /// Converts `x` to its textual representation. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toText(1234) == ("1234" : Text); - /// ``` - public func toText(self : Nat16) : Text { - Nat.toText(toNat(self)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.min(123, 200) == (123 : Nat16); - /// ``` - public func min(x : Nat16, y : Nat16) : Nat16 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.max(123, 200) == (200 : Nat16); - /// ``` - public func max(x : Nat16, y : Nat16) : Nat16 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat16 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.equal(1, 1); - /// assert (1 : Nat16) == (1 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Nat16 = 111; - /// let b : Nat16 = 222; - /// assert not Nat16.equal(a, b); - /// ``` - public func equal(x : Nat16, y : Nat16) : Bool { x == y }; - - /// Inequality function for Nat16 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.notEqual(1, 2); - /// assert (1 : Nat16) != (2 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat16, y : Nat16) : Bool { x != y }; - - /// "Less than" function for Nat16 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.less(1, 2); - /// assert (1 : Nat16) < (2 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat16, y : Nat16) : Bool { x < y }; - - /// "Less than or equal" function for Nat16 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.lessOrEqual(1, 2); - /// assert (1 : Nat16) <= (2 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat16, y : Nat16) : Bool { x <= y }; - - /// "Greater than" function for Nat16 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.greater(2, 1); - /// assert (2 : Nat16) > (1 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat16, y : Nat16) : Bool { x > y }; - - /// "Greater than or equal" function for Nat16 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.greaterOrEqual(2, 1); - /// assert (2 : Nat16) >= (1 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat16, y : Nat16) : Bool { - x >= y - }; - - /// General purpose comparison function for `Nat16`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1] : [Nat16], Nat16.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat16, y : Nat16) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.add(1, 2) == 3; - /// assert (1 : Nat16) + (2 : Nat16) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat16.add) == 6; - /// ``` - public func add(x : Nat16, y : Nat16) : Nat16 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.sub(2, 1) == 1; - /// assert (2 : Nat16) - (1 : Nat16) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 20, Nat16.sub) == 14; - /// ``` - public func sub(x : Nat16, y : Nat16) : Nat16 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.mul(2, 3) == 6; - /// assert (2 : Nat16) * (3 : Nat16) == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat16.mul) == 6; - /// ``` - public func mul(x : Nat16, y : Nat16) : Nat16 { x * y }; - - /// Returns the quotient of `x` divided by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.div(6, 2) == 3; - /// assert (6 : Nat16) / (2 : Nat16) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat16, y : Nat16) : Nat16 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.rem(6, 4) == 2; - /// assert (6 : Nat16) % (4 : Nat16) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat16, y : Nat16) : Nat16 { x % y }; - - /// Returns the power of `x` to `y`, `x ** y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.pow(2, 3) == 8; - /// assert (2 : Nat16) ** (3 : Nat16) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat16, y : Nat16) : Nat16 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitnot(0) == 65535; - /// assert ^(0 : Nat16) == 65535; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat16) : Nat16 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitand(0, 1) == 0; - /// assert (0 : Nat16) & (1 : Nat16) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat16, y : Nat16) : Nat16 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitor(0, 1) == 1; - /// assert (0 : Nat16) | (1 : Nat16) == 1; - /// ``` - public func bitor(x : Nat16, y : Nat16) : Nat16 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitxor(0, 1) == 1; - /// assert (0 : Nat16) ^ (1 : Nat16) == 1; - /// ``` - public func bitxor(x : Nat16, y : Nat16) : Nat16 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitshiftLeft(1, 3) == 8; - /// assert (1 : Nat16) << (3 : Nat16) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat16, y : Nat16) : Nat16 { - x << y - }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitshiftRight(8, 3) == 1; - /// assert (8 : Nat16) >> (3 : Nat16) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat16, y : Nat16) : Nat16 { - x >> y - }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitrotLeft(2, 1) == 4; - /// assert (2 : Nat16) <<> (1 : Nat16) == 4; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat16, y : Nat16) : Nat16 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitrotRight(1, 1) == 32768; - /// assert (1 : Nat16) <>> (1 : Nat16) == 32768; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat16, y : Nat16) : Nat16 { - x <>> y - }; - - /// Returns the value of bit `p mod 16` in `x`, `(x & 2^(p mod 16)) == 2^(p mod 16)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bittest(5, 2); - /// ``` - public func bittest(x : Nat16, p : Nat) : Bool { - Prim.btstNat16(x, Prim.natToNat16(p)) - }; - - /// Returns the value of setting bit `p mod 16` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitset(0, 2) == 4; - /// ``` - public func bitset(x : Nat16, p : Nat) : Nat16 { - x | (1 << Prim.natToNat16(p)) - }; - - /// Returns the value of clearing bit `p mod 16` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitclear(5, 2) == 1; - /// ``` - public func bitclear(x : Nat16, p : Nat) : Nat16 { - x & ^(1 << Prim.natToNat16(p)) - }; - - /// Returns the value of flipping bit `p mod 16` in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitflip(5, 2) == 1; - /// ``` - public func bitflip(x : Nat16, p : Nat) : Nat16 { - x ^ (1 << Prim.natToNat16(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitcountNonZero(5) == 2; - /// ``` - public let bitcountNonZero : (x : Nat16) -> Nat16 = Prim.popcntNat16; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitcountLeadingZero(5) == 13; - /// ``` - public let bitcountLeadingZero : (x : Nat16) -> Nat16 = Prim.clzNat16; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitcountTrailingZero(5) == 0; - /// ``` - public let bitcountTrailingZero : (x : Nat16) -> Nat16 = Prim.ctzNat16; - - /// Returns the upper (i.e. most significant) and lower (least significant) byte of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.explode 0xaa88 == (170, 136); - /// ``` - public let explode : (x : Nat16) -> (msb : Nat8, lsb : Nat8) = Prim.explodeNat16; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.addWrap(65532, 5) == 1; - /// assert (65532 : Nat16) +% (5 : Nat16) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat16, y : Nat16) : Nat16 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.subWrap(1, 2) == 65535; - /// assert (1 : Nat16) -% (2 : Nat16) == 65535; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat16, y : Nat16) : Nat16 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.mulWrap(655, 101) == 619; - /// assert (655 : Nat16) *% (101 : Nat16) == 619; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat16, y : Nat16) : Nat16 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.powWrap(2, 16) == 0; - /// assert (2 : Nat16) **% (16 : Nat16) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat16, y : Nat16) : Nat16 { x **% y }; - - /// Returns an iterator over `Nat16` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat16, toExclusive : Nat16) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat16 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Nat16` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat16, to : Nat16) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Nat16 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Nat16 values, from 0 to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(0, maxValue) - }; - -} diff --git a/.mops/core@2.4.0/src/Nat32.mo b/.mops/core@2.4.0/src/Nat32.mo deleted file mode 100644 index f4759f1..0000000 --- a/.mops/core@2.4.0/src/Nat32.mo +++ /dev/null @@ -1,724 +0,0 @@ -/// Utility functions on 32-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat32 "mo:core/Nat32"; -/// ``` -import Nat "Nat"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 32-bit natural numbers. - public type Nat32 = Prim.Types.Nat32; - - /// Maximum 32-bit natural number. `2 ** 32 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.maxValue == (4294967295 : Nat32); - /// ``` - public let maxValue : Nat32 = 4294967295; - - /// Converts a 32-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toNat(123) == (123 : Nat); - /// ``` - public let toNat : (self : Nat32) -> Nat = Prim.nat32ToNat; - - /// Converts an unsigned integer with infinite precision to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromNat(123) == (123 : Nat32); - /// ``` - public let fromNat : Nat -> Nat32 = Prim.natToNat32; - - /// Converts a 32-bit unsigned integer to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toNat8(123) == (123 : Nat8); - /// ``` - public func toNat8(self : Nat32) : Nat8 { - Prim.nat16ToNat8(Prim.nat32ToNat16(self)) - }; - - /// Converts an 8-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromNat8(123) == (123 : Nat32); - /// ``` - /// @deprecated M0235 - public func fromNat8(x : Nat8) : Nat32 { - Prim.nat16ToNat32(Prim.nat8ToNat16(x)) - }; - - /// Converts a 16-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromNat16(123) == (123 : Nat32); - /// ``` - /// @deprecated M0235 - public let fromNat16 : (x : Nat16) -> Nat32 = Prim.nat16ToNat32; - - /// Converts a 32-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toNat16(123) == (123 : Nat16); - /// ``` - public let toNat16 : (self : Nat32) -> Nat16 = Prim.nat32ToNat16; - - /// Converts a 64-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromNat64(123) == (123 : Nat32); - /// ``` - /// @deprecated M0235 - public let fromNat64 : (x : Nat64) -> Nat32 = Prim.nat64ToNat32; - - /// Converts a 32-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toNat64(123) == (123 : Nat64); - /// ``` - public let toNat64 : (self : Nat32) -> Nat64 = Prim.nat32ToNat64; - - /// Converts a signed integer with infinite precision to a 32-bit unsigned integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromIntWrap(123) == (123 : Nat32); - /// ``` - public let fromIntWrap : Int -> Nat32 = Prim.intToNat32Wrap; - - /// Convert a Nat32 `char` to a Char in its Unicode representation. - /// - /// Example: - /// ```motoko include=import - /// let unicode = Nat32.toChar(65); - /// assert unicode == 'A'; - /// ``` - public let toChar : (self : Nat32) -> Char = Prim.nat32ToChar; - - /// Converts `x` to its textual representation. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toText(1234) == ("1234" : Text); - /// ``` - public func toText(self : Nat32) : Text { - Nat.toText(toNat(self)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.min(123, 456) == (123 : Nat32); - /// ``` - public func min(x : Nat32, y : Nat32) : Nat32 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.max(123, 456) == (456 : Nat32); - /// ``` - public func max(x : Nat32, y : Nat32) : Nat32 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat32 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.equal(1, 1); - /// assert (1 : Nat32) == (1 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Nat32 = 111; - /// let b : Nat32 = 222; - /// assert not Nat32.equal(a, b); - /// ``` - public func equal(x : Nat32, y : Nat32) : Bool { x == y }; - - /// Inequality function for Nat32 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.notEqual(1, 2); - /// assert (1 : Nat32) != (2 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat32, y : Nat32) : Bool { x != y }; - - /// "Less than" function for Nat32 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.less(1, 2); - /// assert (1 : Nat32) < (2 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat32, y : Nat32) : Bool { x < y }; - - /// "Less than or equal" function for Nat32 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.lessOrEqual(1, 2); - /// assert (1 : Nat32) <= (2 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat32, y : Nat32) : Bool { x <= y }; - - /// "Greater than" function for Nat32 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.greater(2, 1); - /// assert (2 : Nat32) > (1 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat32, y : Nat32) : Bool { x > y }; - - /// "Greater than or equal" function for Nat32 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.greaterOrEqual(2, 1); - /// assert (2 : Nat32) >= (1 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat32, y : Nat32) : Bool { - x >= y - }; - - /// General purpose comparison function for `Nat32`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1] : [Nat32], Nat32.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat32, y : Nat32) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.add(1, 2) == 3; - /// assert (1 : Nat32) + (2 : Nat32) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat32.add) == 6; - /// ``` - public func add(x : Nat32, y : Nat32) : Nat32 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.sub(2, 1) == 1; - /// assert (2 : Nat32) - (1 : Nat32) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 20, Nat32.sub) == 14; - /// ``` - public func sub(x : Nat32, y : Nat32) : Nat32 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.mul(2, 3) == 6; - /// assert (2 : Nat32) * (3 : Nat32) == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat32.mul) == 6; - /// ``` - public func mul(x : Nat32, y : Nat32) : Nat32 { x * y }; - - /// Returns the division of `x by y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.div(6, 2) == 3; - /// assert (6 : Nat32) / (2 : Nat32) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat32, y : Nat32) : Nat32 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.rem(6, 4) == 2; - /// assert (6 : Nat32) % (4 : Nat32) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat32, y : Nat32) : Nat32 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.pow(2, 3) == 8; - /// assert (2 : Nat32) ** (3 : Nat32) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat32, y : Nat32) : Nat32 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitnot(0) == 4294967295; - /// assert ^(0 : Nat32) == 4294967295; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat32) : Nat32 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitand(1, 3) == 1; - /// assert (1 : Nat32) & (3 : Nat32) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat32, y : Nat32) : Nat32 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitor(1, 3) == 3; - /// assert (1 : Nat32) | (3 : Nat32) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Nat32, y : Nat32) : Nat32 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitxor(1, 3) == 2; - /// assert (1 : Nat32) ^ (3 : Nat32) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Nat32, y : Nat32) : Nat32 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitshiftLeft(1, 3) == 8; - /// assert (1 : Nat32) << (3 : Nat32) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat32, y : Nat32) : Nat32 { - x << y - }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitshiftRight(8, 3) == 1; - /// assert (8 : Nat32) >> (3 : Nat32) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat32, y : Nat32) : Nat32 { - x >> y - }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitrotLeft(1, 3) == 8; - /// assert (1 : Nat32) <<> (3 : Nat32) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat32, y : Nat32) : Nat32 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitrotRight(1, 1) == 2147483648; - /// assert (1 : Nat32) <>> (1 : Nat32) == 2147483648; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat32, y : Nat32) : Nat32 { - x <>> y - }; - - /// Returns the value of bit `p mod 32` in `x`, `(x & 2^(p mod 32)) == 2^(p mod 32)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bittest(5, 2); - /// ``` - public func bittest(x : Nat32, p : Nat) : Bool { - Prim.btstNat32(x, Prim.natToNat32(p)) - }; - - /// Returns the value of setting bit `p mod 32` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitset(5, 1) == 7; - /// ``` - public func bitset(x : Nat32, p : Nat) : Nat32 { - x | (1 << Prim.natToNat32(p)) - }; - - /// Returns the value of clearing bit `p mod 32` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitclear(5, 2) == 1; - /// ``` - public func bitclear(x : Nat32, p : Nat) : Nat32 { - x & ^(1 << Prim.natToNat32(p)) - }; - - /// Returns the value of flipping bit `p mod 32` in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitflip(5, 2) == 1; - /// ``` - public func bitflip(x : Nat32, p : Nat) : Nat32 { - x ^ (1 << Prim.natToNat32(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitcountNonZero(5) == 2; - /// ``` - public let bitcountNonZero : (x : Nat32) -> Nat32 = Prim.popcntNat32; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitcountLeadingZero(5) == 29; - /// ``` - public let bitcountLeadingZero : (x : Nat32) -> Nat32 = Prim.clzNat32; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitcountTrailingZero(16) == 4; - /// ``` - public let bitcountTrailingZero : (x : Nat32) -> Nat32 = Prim.ctzNat32; - - /// Returns the upper (i.e. most significant), lower (least significant) - /// and in-between bytes of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.explode 0xaa885511 == (170, 136, 85, 17); - /// ``` - public let explode : (x : Nat32) -> (msb : Nat8, Nat8, Nat8, lsb : Nat8) = Prim.explodeNat32; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.addWrap(4294967295, 1) == 0; - /// assert (4294967295 : Nat32) +% (1 : Nat32) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat32, y : Nat32) : Nat32 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.subWrap(0, 1) == 4294967295; - /// assert (0 : Nat32) -% (1 : Nat32) == 4294967295; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat32, y : Nat32) : Nat32 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.mulWrap(2147483648, 2) == 0; - /// assert (2147483648 : Nat32) *% (2 : Nat32) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat32, y : Nat32) : Nat32 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.powWrap(2, 32) == 0; - /// assert (2 : Nat32) **% (32 : Nat32) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat32, y : Nat32) : Nat32 { x **% y }; - - /// Returns an iterator over `Nat32` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat32, toExclusive : Nat32) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat32 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Nat32` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat32, to : Nat32) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Nat32 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Nat32 values, from 0 to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(0, maxValue) - }; - -} diff --git a/.mops/core@2.4.0/src/Nat64.mo b/.mops/core@2.4.0/src/Nat64.mo deleted file mode 100644 index e16a9f1..0000000 --- a/.mops/core@2.4.0/src/Nat64.mo +++ /dev/null @@ -1,719 +0,0 @@ -/// Utility functions on 64-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat64 "mo:core/Nat64"; -/// ``` -import Nat "Nat"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 64-bit natural numbers. - public type Nat64 = Prim.Types.Nat64; - - /// Maximum 64-bit natural number. `2 ** 64 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.maxValue == (18446744073709551615 : Nat64); - /// ``` - public let maxValue : Nat64 = 18446744073709551615; - - /// Converts a 64-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toNat(123) == (123 : Nat); - /// ``` - public let toNat : (self : Nat64) -> Nat = Prim.nat64ToNat; - - /// Converts an unsigned integer with infinite precision to a 64-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromNat(123) == (123 : Nat64); - /// ``` - public let fromNat : Nat -> Nat64 = Prim.natToNat64; - - /// Converts a 64-bit unsigned integer to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toNat8(123) == (123 : Nat8); - /// ``` - public func toNat8(self : Nat64) : Nat8 { - Prim.nat16ToNat8(Prim.nat32ToNat16(Prim.nat64ToNat32(self))) - }; - - /// Converts a 16-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromNat16(123) == (123 : Nat64); - /// ``` - /// @deprecated M0235 - public func fromNat16(x : Nat16) : Nat64 { - Prim.nat32ToNat64(Prim.nat16ToNat32(x)) - }; - - /// Converts a 64-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toNat16(123) == (123 : Nat16); - /// ``` - public func toNat16(self : Nat64) : Nat16 { - Prim.nat32ToNat16(Prim.nat64ToNat32(self)) - }; - - /// Converts an 8-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromNat8(123) == (123 : Nat64); - /// ``` - /// @deprecated M0235 - public func fromNat8(x : Nat8) : Nat64 { - Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(x))) - }; - - /// Converts a 32-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromNat32(123) == (123 : Nat64); - /// ``` - /// @deprecated M0235 - public let fromNat32 : (x : Nat32) -> Nat64 = Prim.nat32ToNat64; - - /// Converts a 64-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toNat32(123) == (123 : Nat32); - /// ``` - public let toNat32 : (self : Nat64) -> Nat32 = Prim.nat64ToNat32; - - /// Converts a signed integer with infinite precision to a 64-bit unsigned integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromIntWrap(123) == (123 : Nat64); - /// ``` - public let fromIntWrap : Int -> Nat64 = Prim.intToNat64Wrap; - - /// Converts `x` to its textual representation. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toText(1234) == ("1234" : Text); - /// ``` - public func toText(self : Nat64) : Text { - Nat.toText(toNat(self)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.min(123, 456) == (123 : Nat64); - /// ``` - public func min(x : Nat64, y : Nat64) : Nat64 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.max(123, 456) == (456 : Nat64); - /// ``` - public func max(x : Nat64, y : Nat64) : Nat64 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat64 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.equal(1, 1); - /// assert (1 : Nat64) == (1 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Nat64 = 111; - /// let b : Nat64 = 222; - /// assert not Nat64.equal(a, b); - /// ``` - public func equal(x : Nat64, y : Nat64) : Bool { x == y }; - - /// Inequality function for Nat64 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.notEqual(1, 2); - /// assert (1 : Nat64) != (2 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat64, y : Nat64) : Bool { x != y }; - - /// "Less than" function for Nat64 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.less(1, 2); - /// assert (1 : Nat64) < (2 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat64, y : Nat64) : Bool { x < y }; - - /// "Less than or equal" function for Nat64 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.lessOrEqual(1, 2); - /// assert (1 : Nat64) <= (2 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat64, y : Nat64) : Bool { x <= y }; - - /// "Greater than" function for Nat64 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.greater(2, 1); - /// assert (2 : Nat64) > (1 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat64, y : Nat64) : Bool { x > y }; - - /// "Greater than or equal" function for Nat64 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.greaterOrEqual(2, 1); - /// assert (2 : Nat64) >= (1 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat64, y : Nat64) : Bool { - x >= y - }; - - /// General purpose comparison function for `Nat64`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1] : [Nat64], Nat64.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat64, y : Nat64) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.add(1, 2) == 3; - /// assert (1 : Nat64) + (2 : Nat64) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat64.add) == 6; - /// ``` - public func add(x : Nat64, y : Nat64) : Nat64 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.sub(3, 1) == 2; - /// assert (3 : Nat64) - (1 : Nat64) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 10, Nat64.sub) == 4; - /// ``` - public func sub(x : Nat64, y : Nat64) : Nat64 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.mul(2, 3) == 6; - /// assert (2 : Nat64) * (3 : Nat64) == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat64.mul) == 6; - /// ``` - public func mul(x : Nat64, y : Nat64) : Nat64 { x * y }; - - /// Returns the quotient of `x` divided by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.div(6, 2) == 3; - /// assert (6 : Nat64) / (2 : Nat64) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat64, y : Nat64) : Nat64 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.rem(6, 4) == 2; - /// assert (6 : Nat64) % (4 : Nat64) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat64, y : Nat64) : Nat64 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.pow(2, 3) == 8; - /// assert (2 : Nat64) ** (3 : Nat64) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat64, y : Nat64) : Nat64 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitnot(0) == 18446744073709551615; - /// assert ^(0 : Nat64) == 18446744073709551615; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat64) : Nat64 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitand(1, 3) == 1; - /// assert (1 : Nat64) & (3 : Nat64) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat64, y : Nat64) : Nat64 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitor(1, 3) == 3; - /// assert (1 : Nat64) | (3 : Nat64) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Nat64, y : Nat64) : Nat64 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitxor(1, 3) == 2; - /// assert (1 : Nat64) ^ (3 : Nat64) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Nat64, y : Nat64) : Nat64 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitshiftLeft(1, 3) == 8; - /// assert (1 : Nat64) << (3 : Nat64) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat64, y : Nat64) : Nat64 { - x << y - }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitshiftRight(8, 3) == 1; - /// assert (8 : Nat64) >> (3 : Nat64) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat64, y : Nat64) : Nat64 { - x >> y - }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitrotLeft(1, 3) == 8; - /// assert (1 : Nat64) <<> (3 : Nat64) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat64, y : Nat64) : Nat64 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitrotRight(8, 3) == 1; - /// assert (8 : Nat64) <>> (3 : Nat64) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat64, y : Nat64) : Nat64 { - x <>> y - }; - - /// Returns the value of bit `p mod 64` in `x`, `(x & 2^(p mod 64)) == 2^(p mod 64)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bittest(5, 2); - /// ``` - public func bittest(x : Nat64, p : Nat) : Bool { - Prim.btstNat64(x, Prim.natToNat64(p)) - }; - - /// Returns the value of setting bit `p mod 64` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitset(5, 1) == 7; - /// ``` - public func bitset(x : Nat64, p : Nat) : Nat64 { - x | (1 << Prim.natToNat64(p)) - }; - - /// Returns the value of clearing bit `p mod 64` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitclear(5, 2) == 1; - /// ``` - public func bitclear(x : Nat64, p : Nat) : Nat64 { - x & ^(1 << Prim.natToNat64(p)) - }; - - /// Returns the value of flipping bit `p mod 64` in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitflip(5, 2) == 1; - /// ``` - public func bitflip(x : Nat64, p : Nat) : Nat64 { - x ^ (1 << Prim.natToNat64(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitcountNonZero(5) == 2; - /// ``` - public let bitcountNonZero : (x : Nat64) -> Nat64 = Prim.popcntNat64; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitcountLeadingZero(5) == 61; - /// ``` - public let bitcountLeadingZero : (x : Nat64) -> Nat64 = Prim.clzNat64; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitcountTrailingZero(16) == 4; - /// ``` - public let bitcountTrailingZero : (x : Nat64) -> Nat64 = Prim.ctzNat64; - - /// Returns the upper (i.e. most significant), lower (least significant) - /// and in-between bytes of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.explode 0xbb772266aa885511 == (187, 119, 34, 102, 170, 136, 85, 17); - /// ``` - public let explode : (x : Nat64) -> (msb : Nat8, Nat8, Nat8, Nat8, Nat8, Nat8, Nat8, lsb : Nat8) = Prim.explodeNat64; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.addWrap(Nat64.maxValue, 1) == 0; - /// assert Nat64.maxValue +% (1 : Nat64) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat64, y : Nat64) : Nat64 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.subWrap(0, 1) == 18446744073709551615; - /// assert (0 : Nat64) -% (1 : Nat64) == 18446744073709551615; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat64, y : Nat64) : Nat64 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.mulWrap(4294967296, 4294967296) == 0; - /// assert (4294967296 : Nat64) *% (4294967296 : Nat64) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat64, y : Nat64) : Nat64 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.powWrap(2, 64) == 0; - /// assert (2 : Nat64) **% (64 : Nat64) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat64, y : Nat64) : Nat64 { x **% y }; - - /// Returns an iterator over `Nat64` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat64, toExclusive : Nat64) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat64 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Nat64` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat64, to : Nat64) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Nat64 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Nat64 values, from 0 to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(0, maxValue) - }; - -} diff --git a/.mops/core@2.4.0/src/Nat8.mo b/.mops/core@2.4.0/src/Nat8.mo deleted file mode 100644 index 429aa7e..0000000 --- a/.mops/core@2.4.0/src/Nat8.mo +++ /dev/null @@ -1,698 +0,0 @@ -/// Utility functions on 8-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat8 "mo:core/Nat8"; -/// ``` -import Nat "Nat"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 8-bit natural numbers. - public type Nat8 = Prim.Types.Nat8; - - /// Maximum 8-bit natural number. `2 ** 8 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.maxValue == (255 : Nat8); - /// ``` - public let maxValue : Nat8 = 255; - - /// Converts an 8-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toNat(123) == (123 : Nat); - /// ``` - public let toNat : (self : Nat8) -> Nat = Prim.nat8ToNat; - - /// Converts an unsigned integer with infinite precision to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromNat(123) == (123 : Nat8); - /// ``` - public let fromNat : Nat -> Nat8 = Prim.natToNat8; - - /// Converts a 16-bit unsigned integer to a 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromNat16(123) == (123 : Nat8); - /// ``` - public let fromNat16 : Nat16 -> Nat8 = Prim.nat16ToNat8; - - /// Converts an 8-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toNat16(123) == (123 : Nat16); - /// ``` - public let toNat16 : (self : Nat8) -> Nat16 = Prim.nat8ToNat16; - - /// Converts a 32-bit unsigned integer to a 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromNat32(123) == (123 : Nat8); - /// ``` - public func fromNat32(x : Nat32) : Nat8 { - Prim.nat16ToNat8(Prim.nat32ToNat16(x)) - }; - - /// Converts an 8-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toNat32(123) == (123 : Nat32); - /// ``` - public func toNat32(self : Nat8) : Nat32 { - Prim.nat16ToNat32(Prim.nat8ToNat16(self)) - }; - - /// Converts a 64-bit unsigned integer to a 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromNat64(123) == (123 : Nat8); - /// ``` - public func fromNat64(x : Nat64) : Nat8 { - Prim.nat16ToNat8(Prim.nat32ToNat16(Prim.nat64ToNat32(x))) - }; - - /// Converts an 8-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toNat64(123) == (123 : Nat64); - /// ``` - public func toNat64(self : Nat8) : Nat64 { - Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(self))) - }; - - /// Converts a signed integer with infinite precision to an 8-bit unsigned integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromIntWrap(123) == (123 : Nat8); - /// ``` - public let fromIntWrap : Int -> Nat8 = Prim.intToNat8Wrap; - - /// Converts `x` to its textual representation. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toText(123) == ("123" : Text); - /// ``` - public func toText(self : Nat8) : Text { - Nat.toText(toNat(self)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.min(123, 200) == (123 : Nat8); - /// ``` - public func min(x : Nat8, y : Nat8) : Nat8 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.max(123, 200) == (200 : Nat8); - /// ``` - public func max(x : Nat8, y : Nat8) : Nat8 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat8 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.equal(1, 1); - /// assert (1 : Nat8) == (1 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Nat8 = 111; - /// let b : Nat8 = 222; - /// assert not Nat8.equal(a, b); - /// ``` - public func equal(x : Nat8, y : Nat8) : Bool { x == y }; - - /// Inequality function for Nat8 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.notEqual(1, 2); - /// assert (1 : Nat8) != (2 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat8, y : Nat8) : Bool { x != y }; - - /// "Less than" function for Nat8 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.less(1, 2); - /// assert (1 : Nat8) < (2 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat8, y : Nat8) : Bool { x < y }; - - /// "Less than or equal" function for Nat8 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.lessOrEqual(1, 2); - /// assert 1 <= 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat8, y : Nat8) : Bool { x <= y }; - - /// "Greater than" function for Nat8 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.greater(2, 1); - /// assert (2 : Nat8) > (1 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat8, y : Nat8) : Bool { x > y }; - - /// "Greater than or equal" function for Nat8 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.greaterOrEqual(2, 1); - /// assert (2 : Nat8) >= (1 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat8, y : Nat8) : Bool { x >= y }; - - /// General purpose comparison function for `Nat8`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1] : [Nat8], Nat8.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat8, y : Nat8) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.add(1, 2) == 3; - /// assert (1 : Nat8) + (2 : Nat8) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat8.add) == 6; - /// ``` - public func add(x : Nat8, y : Nat8) : Nat8 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.sub(2, 1) == 1; - /// assert (2 : Nat8) - (1 : Nat8) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 20, Nat8.sub) == 14; - /// ``` - public func sub(x : Nat8, y : Nat8) : Nat8 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.mul(2, 3) == 6; - /// assert (2 : Nat8) * (3 : Nat8) == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat8.mul) == 6; - /// ``` - public func mul(x : Nat8, y : Nat8) : Nat8 { x * y }; - - /// Returns the quotient of `x` divided by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.div(6, 2) == 3; - /// assert (6 : Nat8) / (2 : Nat8) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat8, y : Nat8) : Nat8 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.rem(6, 4) == 2; - /// assert (6 : Nat8) % (4 : Nat8) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat8, y : Nat8) : Nat8 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.pow(2, 3) == 8; - /// assert (2 : Nat8) ** (3 : Nat8) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat8, y : Nat8) : Nat8 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitnot(0) == 255; - /// assert ^(0 : Nat8) == 255; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat8) : Nat8 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitand(3, 2) == 2; - /// assert (3 : Nat8) & (2 : Nat8) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat8, y : Nat8) : Nat8 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitor(3, 2) == 3; - /// assert (3 : Nat8) | (2 : Nat8) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Nat8, y : Nat8) : Nat8 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitxor(3, 2) == 1; - /// assert (3 : Nat8) ^ (2 : Nat8) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Nat8, y : Nat8) : Nat8 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitshiftLeft(1, 2) == 4; - /// assert (1 : Nat8) << (2 : Nat8) == 4; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat8, y : Nat8) : Nat8 { x << y }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitshiftRight(4, 2) == 1; - /// assert (4 : Nat8) >> (2 : Nat8) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat8, y : Nat8) : Nat8 { x >> y }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitrotLeft(128, 1) == 1; - /// assert (128 : Nat8) <<> (1 : Nat8) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat8, y : Nat8) : Nat8 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitrotRight(1, 1) == 128; - /// assert (1 : Nat8) <>> (1 : Nat8) == 128; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat8, y : Nat8) : Nat8 { x <>> y }; - - /// Returns the value of bit `p mod 8` in `x`, `(x & 2^(p mod 8)) == 2^(p mod 8)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bittest(5, 2); - /// ``` - public func bittest(x : Nat8, p : Nat) : Bool { - Prim.btstNat8(x, Prim.natToNat8(p)) - }; - - /// Returns the value of setting bit `p mod 8` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitset(5, 1) == 7; - /// ``` - public func bitset(x : Nat8, p : Nat) : Nat8 { - x | (1 << Prim.natToNat8(p)) - }; - - /// Returns the value of clearing bit `p mod 8` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitclear(5, 2) == 1; - /// ``` - public func bitclear(x : Nat8, p : Nat) : Nat8 { - x & ^(1 << Prim.natToNat8(p)) - }; - - /// Returns the value of flipping bit `p mod 8` in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitflip(5, 2) == 1; - /// ``` - public func bitflip(x : Nat8, p : Nat) : Nat8 { - x ^ (1 << Prim.natToNat8(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitcountNonZero(5) == 2; - /// ``` - public let bitcountNonZero : (x : Nat8) -> Nat8 = Prim.popcntNat8; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitcountLeadingZero(5) == 5; - /// ``` - public let bitcountLeadingZero : (x : Nat8) -> Nat8 = Prim.clzNat8; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitcountTrailingZero(6) == 1; - /// ``` - public let bitcountTrailingZero : (x : Nat8) -> Nat8 = Prim.ctzNat8; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.addWrap(230, 26) == 0; - /// assert (230 : Nat8) +% (26 : Nat8) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat8, y : Nat8) : Nat8 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.subWrap(0, 1) == 255; - /// assert (0 : Nat8) -% (1 : Nat8) == 255; - /// ``` - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat8, y : Nat8) : Nat8 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.mulWrap(230, 26) == 92; - /// assert (230 : Nat8) *% (26 : Nat8) == 92; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat8, y : Nat8) : Nat8 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.powWrap(2, 8) == 0; - /// assert (2 : Nat8) **% (8 : Nat8) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat8, y : Nat8) : Nat8 { x **% y }; - - /// Returns an iterator over `Nat8` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat8, toExclusive : Nat8) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat8 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Nat8` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat8, to : Nat8) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Nat8 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Nat8 values, from 0 to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(0, maxValue) - }; - -} diff --git a/.mops/core@2.4.0/src/Option.mo b/.mops/core@2.4.0/src/Option.mo deleted file mode 100644 index 27bfce6..0000000 --- a/.mops/core@2.4.0/src/Option.mo +++ /dev/null @@ -1,154 +0,0 @@ -/// Typesafe nullable values. -/// -/// Optional values can be seen as a typesafe `null`. A value of type `?Int` can -/// be constructed with either `null` or `?42`. The simplest way to get at the -/// contents of an optional is to use pattern matching: -/// -/// ```motoko -/// let optionalInt1 : ?Int = ?42; -/// let optionalInt2 : ?Int = null; -/// -/// let int1orZero : Int = switch optionalInt1 { -/// case null 0; -/// case (?int) int; -/// }; -/// assert int1orZero == 42; -/// -/// let int2orZero : Int = switch optionalInt2 { -/// case null 0; -/// case (?int) int; -/// }; -/// assert int2orZero == 0; -/// ``` -/// -/// The functions in this module capture some common operations when working -/// with optionals that can be more succinct than using pattern matching. - -import Runtime "Runtime"; -import Types "Types"; - -module { - - /// Unwraps an optional value, with a default value, i.e. `get(?x, d) = x` and - /// `get(null, d) = d`. - public func get(self : ?T, default : T) : T = switch self { - case null { default }; - case (?x_) { x_ } - }; - - /// Unwraps an optional value using a function, or returns the default, i.e. - /// `option(?x, f, d) = f x` and `option(null, f, d) = d`. - public func getMapped(self : ?T, f : T -> R, default : R) : R = switch self { - case null { default }; - case (?x_) { f(x_) } - }; - - /// Applies a function to the wrapped value. `null`'s are left untouched. - /// ```motoko - /// import Option "mo:core/Option"; - /// assert Option.map(?42, func x = x + 1) == ?43; - /// assert Option.map(null, func x = x + 1) == null; - /// ``` - public func map(self : ?T, f : T -> R) : ?R = switch self { - case null { null }; - case (?x_) { ?f(x_) } - }; - - /// Applies a function to the wrapped value, but discards the result. Use - /// `forEach` if you're only interested in the side effect `f` produces. - /// - /// ```motoko - /// import Option "mo:core/Option"; - /// var counter : Nat = 0; - /// Option.forEach(?5, func (x : Nat) { counter += x }); - /// assert counter == 5; - /// Option.forEach(null, func (x : Nat) { counter += x }); - /// assert counter == 5; - /// ``` - public func forEach(self : ?T, f : T -> ()) = switch self { - case null {}; - case (?x_) { f(x_) } - }; - - /// Applies an optional function to an optional value. Returns `null` if at - /// least one of the arguments is `null`. - public func apply(self : ?T, f : ?(T -> R)) : ?R { - switch (f, self) { - case (?f_, ?x_) { ?f_(x_) }; - case (_, _) { null } - } - }; - - /// Applies a function to an optional value. Returns `null` if the argument is - /// `null`, or the function returns `null`. - public func chain(self : ?T, f : T -> ?R) : ?R { - switch (self) { - case (?x_) { f(x_) }; - case (null) { null } - } - }; - - /// Given an optional optional value, removes one layer of optionality. - /// ```motoko - /// import Option "mo:core/Option"; - /// assert Option.flatten(?(?(42))) == ?42; - /// assert Option.flatten(?(null)) == null; - /// assert Option.flatten(null) == null; - /// ``` - public func flatten(self : ??T) : ?T { - chain(self, func(x_ : ?T) : ?T = x_) - }; - - /// Creates an optional value from a definite value. - /// ```motoko - /// import Option "mo:core/Option"; - /// assert Option.some(42) == ?42; - /// ``` - public func some(self : T) : ?T = ?self; - - /// Returns true if the argument is not `null`, otherwise returns false. - public func isSome(self : ?Any) : Bool { - self != null - }; - - /// Returns true if the argument is `null`, otherwise returns false. - public func isNull(self : ?Any) : Bool { - self == null - }; - - /// Returns true if the optional arguments are equal according to the equality function provided, otherwise returns false. - public func equal(self : ?T, other : ?T, eq : (implicit : (equal : (T, T) -> Bool))) : Bool = switch (self, other) { - case (null, null) { true }; - case (?x_, ?y_) { eq(x_, y_) }; - case (_, _) { false } - }; - - /// Compares two optional values using the provided comparison function. - /// - /// Returns: - /// - `#equal` if both values are `null`, - /// - `#less` if the first value is `null` and the second is not, - /// - `#greater` if the first value is not `null` and the second is, - /// - the result of the comparison function when both values are not `null`. - public func compare(self : ?T, other : ?T, compare : (implicit : (T, T) -> Types.Order)) : Types.Order = switch (self, other) { - case (null, null) #equal; - case (null, _) #less; - case (_, null) #greater; - case (?x_, ?y_) { compare(x_, y_) } - }; - - /// Unwraps an optional value, i.e. `unwrap(?x) = x`. - /// - /// `Option.unwrap()` fails if the argument is null. Consider using a `switch` or `do?` expression instead. - public func unwrap(self : ?T) : T = switch self { - case null { Runtime.trap("Option.unwrap()") }; - case (?x_) { x_ } - }; - - /// Returns the textural representation of an optional value for debugging purposes. - public func toText(self : ?T, toText : (implicit : T -> Text)) : Text = switch self { - case null { "null" }; - case (?x_) { "?" # toText(x_) } - }; - -} diff --git a/.mops/core@2.4.0/src/Order.mo b/.mops/core@2.4.0/src/Order.mo deleted file mode 100644 index d708a11..0000000 --- a/.mops/core@2.4.0/src/Order.mo +++ /dev/null @@ -1,62 +0,0 @@ -/// Utilities for `Order` (comparison between two values). - -import Types "Types"; - -module { - - /// A type to represent an order. - public type Order = Types.Order; - - /// Check if an order is #less. - public func isLess(self : Order) : Bool { - switch self { - case (#less) { true }; - case _ { false } - } - }; - - /// Check if an order is #equal. - public func isEqual(self : Order) : Bool { - switch self { - case (#equal) { true }; - case _ { false } - } - }; - - /// Check if an order is #greater. - public func isGreater(self : Order) : Bool { - switch self { - case (#greater) { true }; - case _ { false } - } - }; - - /// Returns true if only if `order1` and `order2` are the same. - public func equal(self : Order, other : Order) : Bool { - switch (self, other) { - case (#less, #less) { true }; - case (#equal, #equal) { true }; - case (#greater, #greater) { true }; - case _ { false } - } - }; - - /// Returns an iterator that yields all possible `Order` values: - /// `#less`, `#equal`, `#greater`. - public func allValues() : Types.Iter { - var nextState : ?Order = ?#less; - { - next = func() : ?Order { - let state = nextState; - switch state { - case (?#less) { nextState := ?#equal }; - case (?#equal) { nextState := ?#greater }; - case (?#greater) { nextState := null }; - case (null) {} - }; - state - } - } - } - -} diff --git a/.mops/core@2.4.0/src/Principal.mo b/.mops/core@2.4.0/src/Principal.mo deleted file mode 100644 index d589243..0000000 --- a/.mops/core@2.4.0/src/Principal.mo +++ /dev/null @@ -1,1284 +0,0 @@ -/// Module for interacting with Principals (users and canisters). -/// -/// Principals are used to identify entities that can interact with the Internet -/// Computer. These entities are either users or canisters. -/// -/// Example textual representation of Principals: -/// -/// `un4fu-tqaaa-aaaab-qadjq-cai` -/// -/// In Motoko, there is a primitive Principal type called `Principal`. As an example -/// of where you might see Principals, you can access the Principal of the -/// caller of your shared function. -/// -/// ```motoko no-repl -/// persistent actor { -/// public shared(msg) func foo() { -/// let caller : Principal = msg.caller; -/// }; -/// } -/// ``` -/// -/// Then, you can use this module to work with the `Principal`. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Principal "mo:core/Principal"; -/// ``` - -import Prim "mo:⛔"; -import Blob "Blob"; -import Array "Array"; -import VarArray "VarArray"; -import Nat8 "Nat8"; -import Nat32 "Nat32"; -import Nat64 "Nat64"; -import Text "Text"; -import Types "Types"; - -module { - - public type Principal = Prim.Types.Principal; - - /// Get the `Principal` identifier of an actor. - /// - /// Example: - /// ```motoko include=import no-repl - /// persistent actor MyCanister { - /// func getPrincipal() : Principal { - /// let principal = Principal.fromActor(MyCanister); - /// } - /// } - /// ``` - public let fromActor : (a : actor {}) -> Principal = Prim.principalOfActor; - - /// Compute the Ledger account identifier of a principal. Optionally specify a sub-account. - /// - /// Example: - /// ```motoko include=import no-validate - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let subAccount : Blob = "\4A\8D\3F\2B\6E\01\C8\7D\9E\03\B4\56\7C\F8\9A\01\D2\34\56\78\9A\BC\DE\F0\12\34\56\78\9A\BC\DE\F0"; - /// let account = Principal.toLedgerAccount(principal, ?subAccount); - /// assert account == "\8C\5C\20\C6\15\3F\7F\51\E2\0D\0F\0F\B5\08\51\5B\47\65\63\A9\62\B4\A9\91\5F\4F\02\70\8A\ED\4F\82"; - /// ``` - public func toLedgerAccount(self : Principal, subAccount : ?Blob) : Blob { - let sha224 = SHA224(); - let accountSeparator : Blob = "\0Aaccount-id"; - sha224.writeBlob(accountSeparator); - sha224.writeBlob(toBlob(self)); - switch subAccount { - case (?subAccount) { - sha224.writeBlob(subAccount) - }; - case (null) { - let defaultSubAccount = Array.tabulate(32, func _ = 0); - sha224.writeArray(defaultSubAccount) - } - }; - - let hashSum = sha224.sum(); - - // hashBlob is a CRC32 implementation - let crc32Bytes = nat32ToByteArray(Prim.hashBlob hashSum); - - Blob.fromArray(Array.concat(crc32Bytes, Blob.toArray(hashSum))) - }; - - /// Convert a `Principal` to its `Blob` (bytes) representation. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let blob = Principal.toBlob(principal); - /// assert blob == "\00\00\00\00\00\30\00\D3\01\01"; - /// ``` - public let toBlob : (self : Principal) -> Blob = Prim.blobOfPrincipal; - - /// Converts a `Blob` (bytes) representation of a `Principal` to a `Principal` value. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\00\00\00\00\00\30\00\D3\01\01" : Blob; - /// let principal = Principal.fromBlob(blob); - /// assert Principal.toText(principal) == "un4fu-tqaaa-aaaab-qadjq-cai"; - /// ``` - public let fromBlob : (self : Blob) -> Principal = Prim.principalOfBlob; - - /// Converts a `Principal` to its `Text` representation. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.toText(principal) == "un4fu-tqaaa-aaaab-qadjq-cai"; - /// ``` - public func toText(self : Principal) : Text = debug_show (self); - - /// Converts a `Text` representation of a `Principal` to a `Principal` value. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.toText(principal) == "un4fu-tqaaa-aaaab-qadjq-cai"; - /// ``` - public func fromText(t : Text) : Principal = fromActor(actor (t)); - - private let anonymousBlob : Blob = "\04"; - - /// Constructs and returns the anonymous principal. - public func anonymous() : Principal = Prim.principalOfBlob(anonymousBlob); - - /// Checks if the given principal represents an anonymous user. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert not Principal.isAnonymous(principal); - /// ``` - public func isAnonymous(self : Principal) : Bool = Prim.blobOfPrincipal self == anonymousBlob; - - /// Checks if the given principal is a canister. - /// - /// The last byte for opaque principal ids must be 0x01 - /// https://internetcomputer.org/docs/current/references/ic-interface-spec#principal - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.isCanister(principal); - /// ``` - public func isCanister(self : Principal) : Bool { - let byteArray = toByteArray(self); - - byteArray.size() >= 0 and byteArray.size() <= 29 and isLastByte(byteArray, 1) - }; - - /// Checks if the given principal is a self authenticating principal. - /// Most of the time, this is a user principal. - /// - /// The last byte for user principal ids must be 0x02 - /// https://internetcomputer.org/docs/current/references/ic-interface-spec#principal - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("6rgy7-3uukz-jrj2k-crt3v-u2wjm-dmn3t-p26d6-ndilt-3gusv-75ybk-jae"); - /// assert Principal.isSelfAuthenticating(principal); - /// ``` - public func isSelfAuthenticating(self : Principal) : Bool { - let byteArray = toByteArray(self); - - byteArray.size() == 29 and isLastByte(byteArray, 2) - }; - - /// Checks if the given principal is a reserved principal. - /// - /// The last byte for reserved principal ids must be 0x7f - /// https://internetcomputer.org/docs/current/references/ic-interface-spec#principal - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert not Principal.isReserved(principal); - /// ``` - public func isReserved(self : Principal) : Bool { - let byteArray = toByteArray(self); - - byteArray.size() >= 0 and byteArray.size() <= 29 and isLastByte(byteArray, 127) - }; - - /// Checks if the given principal can control this canister. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert not Principal.isController(principal); - /// ``` - public func isController(self : Principal) : Bool = Prim.isController self; - - /// Hashes the given principal by hashing its `Blob` representation. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.hash(principal) == 2_742_573_646; - /// ``` - public func hash(self : Principal) : Types.Hash = Blob.hash(Prim.blobOfPrincipal(self)); - - /// General purpose comparison function for `Principal`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `principal1` with - /// `principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.compare(principal1, principal2) == #equal; - /// ``` - public func compare(self : Principal, other : Principal) : { - #less; - #equal; - #greater - } { - if (self < other) { - #less - } else if (self == other) { - #equal - } else { - #greater - } - }; - - /// Equality function for Principal types. - /// This is equivalent to `principal1 == principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.equal(principal1, principal2); - /// assert principal1 == principal2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.anonymous(); - /// let principal2 = Principal.fromBlob("\04"); - /// assert Principal.equal(principal1, principal2); - /// ``` - public func equal(self : Principal, other : Principal) : Bool { - self == other - }; - - /// Inequality function for Principal types. - /// This is equivalent to `principal1 != principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.notEqual(principal1, principal2); - /// assert not (principal1 != principal2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(self : Principal, other : Principal) : Bool { - self != other - }; - - /// "Less than" function for Principal types. - /// This is equivalent to `principal1 < principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.less(principal1, principal2); - /// assert not (principal1 < principal2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(self : Principal, other : Principal) : Bool { - self < other - }; - - /// "Less than or equal to" function for Principal types. - /// This is equivalent to `principal1 <= principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.lessOrEqual(principal1, principal2); - /// assert principal1 <= principal2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(self : Principal, other : Principal) : Bool { - self <= other - }; - - /// "Greater than" function for Principal types. - /// This is equivalent to `principal1 > principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.greater(principal1, principal2); - /// assert not (principal1 > principal2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(self : Principal, other : Principal) : Bool { - self > other - }; - - /// "Greater than or equal to" function for Principal types. - /// This is equivalent to `principal1 >= principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.greaterOrEqual(principal1, principal2); - /// assert principal1 >= principal2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(self : Principal, other : Principal) : Bool { - self >= other - }; - - /** - * SHA224 Utilities used in toAccount(). - * Utilities are not exposed as public functions. - * Taken with permission from https://github.com/research-ag/sha2 - **/ - let K00 : Nat32 = 0x428a2f98; - let K01 : Nat32 = 0x71374491; - let K02 : Nat32 = 0xb5c0fbcf; - let K03 : Nat32 = 0xe9b5dba5; - let K04 : Nat32 = 0x3956c25b; - let K05 : Nat32 = 0x59f111f1; - let K06 : Nat32 = 0x923f82a4; - let K07 : Nat32 = 0xab1c5ed5; - let K08 : Nat32 = 0xd807aa98; - let K09 : Nat32 = 0x12835b01; - let K10 : Nat32 = 0x243185be; - let K11 : Nat32 = 0x550c7dc3; - let K12 : Nat32 = 0x72be5d74; - let K13 : Nat32 = 0x80deb1fe; - let K14 : Nat32 = 0x9bdc06a7; - let K15 : Nat32 = 0xc19bf174; - let K16 : Nat32 = 0xe49b69c1; - let K17 : Nat32 = 0xefbe4786; - let K18 : Nat32 = 0x0fc19dc6; - let K19 : Nat32 = 0x240ca1cc; - let K20 : Nat32 = 0x2de92c6f; - let K21 : Nat32 = 0x4a7484aa; - let K22 : Nat32 = 0x5cb0a9dc; - let K23 : Nat32 = 0x76f988da; - let K24 : Nat32 = 0x983e5152; - let K25 : Nat32 = 0xa831c66d; - let K26 : Nat32 = 0xb00327c8; - let K27 : Nat32 = 0xbf597fc7; - let K28 : Nat32 = 0xc6e00bf3; - let K29 : Nat32 = 0xd5a79147; - let K30 : Nat32 = 0x06ca6351; - let K31 : Nat32 = 0x14292967; - let K32 : Nat32 = 0x27b70a85; - let K33 : Nat32 = 0x2e1b2138; - let K34 : Nat32 = 0x4d2c6dfc; - let K35 : Nat32 = 0x53380d13; - let K36 : Nat32 = 0x650a7354; - let K37 : Nat32 = 0x766a0abb; - let K38 : Nat32 = 0x81c2c92e; - let K39 : Nat32 = 0x92722c85; - let K40 : Nat32 = 0xa2bfe8a1; - let K41 : Nat32 = 0xa81a664b; - let K42 : Nat32 = 0xc24b8b70; - let K43 : Nat32 = 0xc76c51a3; - let K44 : Nat32 = 0xd192e819; - let K45 : Nat32 = 0xd6990624; - let K46 : Nat32 = 0xf40e3585; - let K47 : Nat32 = 0x106aa070; - let K48 : Nat32 = 0x19a4c116; - let K49 : Nat32 = 0x1e376c08; - let K50 : Nat32 = 0x2748774c; - let K51 : Nat32 = 0x34b0bcb5; - let K52 : Nat32 = 0x391c0cb3; - let K53 : Nat32 = 0x4ed8aa4a; - let K54 : Nat32 = 0x5b9cca4f; - let K55 : Nat32 = 0x682e6ff3; - let K56 : Nat32 = 0x748f82ee; - let K57 : Nat32 = 0x78a5636f; - let K58 : Nat32 = 0x84c87814; - let K59 : Nat32 = 0x8cc70208; - let K60 : Nat32 = 0x90befffa; - let K61 : Nat32 = 0xa4506ceb; - let K62 : Nat32 = 0xbef9a3f7; - let K63 : Nat32 = 0xc67178f2; - - let ivs : [[Nat32]] = [ - [ - // 224 - 0xc1059ed8, - 0x367cd507, - 0x3070dd17, - 0xf70e5939, - 0xffc00b31, - 0x68581511, - 0x64f98fa7, - 0xbefa4fa4 - ], - [ - // 256 - 0x6a09e667, - 0xbb67ae85, - 0x3c6ef372, - 0xa54ff53a, - 0x510e527f, - 0x9b05688c, - 0x1f83d9ab, - 0x5be0cd19 - ] - ]; - - let rot = Nat32.bitrotRight; - - class SHA224() { - let (sum_bytes, iv) = (28, 0); - - var s0 : Nat32 = 0; - var s1 : Nat32 = 0; - var s2 : Nat32 = 0; - var s3 : Nat32 = 0; - var s4 : Nat32 = 0; - var s5 : Nat32 = 0; - var s6 : Nat32 = 0; - var s7 : Nat32 = 0; - - let msg : [var Nat32] = VarArray.repeat(0, 16); - let digest = VarArray.repeat(0, sum_bytes); - var word : Nat32 = 0; - - var i_msg : Nat8 = 0; - var i_byte : Nat8 = 4; - var i_block : Nat64 = 0; - - public func reset() { - i_msg := 0; - i_byte := 4; - i_block := 0; - s0 := ivs[iv][0]; - s1 := ivs[iv][1]; - s2 := ivs[iv][2]; - s3 := ivs[iv][3]; - s4 := ivs[iv][4]; - s5 := ivs[iv][5]; - s6 := ivs[iv][6]; - s7 := ivs[iv][7] - }; - - reset(); - - private func writeByte(val : Nat8) : () { - word := (word << 8) ^ Nat32.fromIntWrap(Nat8.toNat(val)); - i_byte -%= 1; - if (i_byte == 0) { - msg[Nat8.toNat(i_msg)] := word; - word := 0; - i_byte := 4; - i_msg +%= 1; - if (i_msg == 16) { - process_block(); - i_msg := 0; - i_block +%= 1 - } - } - }; - - private func process_block() : () { - let w00 = msg[0]; - let w01 = msg[1]; - let w02 = msg[2]; - let w03 = msg[3]; - let w04 = msg[4]; - let w05 = msg[5]; - let w06 = msg[6]; - let w07 = msg[7]; - let w08 = msg[8]; - let w09 = msg[9]; - let w10 = msg[10]; - let w11 = msg[11]; - let w12 = msg[12]; - let w13 = msg[13]; - let w14 = msg[14]; - let w15 = msg[15]; - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - /* - for ((i, j, k, l, m) in expansion_rounds.values()) { - // (j,k,l,m) = (i+1,i+9,i+14,i+16) - let (v0, v1) = (msg[j], msg[l]); - let s0 = rot(v0, 07) ^ rot(v0, 18) ^ (v0 >> 03); - let s1 = rot(v1, 17) ^ rot(v1, 19) ^ (v1 >> 10); - msg[m] := msg[i] +% s0 +% msg[k] +% s1; - }; - */ - // compress - var a = s0; - var b = s1; - var c = s2; - var d = s3; - var e = s4; - var f = s5; - var g = s6; - var h = s7; - var t = 0 : Nat32; - - t := h +% K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K09 +% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K10 +% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K11 +% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K12 +% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K13 +% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K14 +% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - - /* - for (i in compression_rounds.keys()) { - let ch = (e & f) ^ (^ e & g); - let maj = (a & b) ^ (a & c) ^ (b & c); - let sigma0 = rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - let sigma1 = rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - let t = h +% K[i] +% msg[i] +% ch +% sigma1; - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% maj +% sigma0; - }; - */ - // final addition - s0 +%= a; - s1 +%= b; - s2 +%= c; - s3 +%= d; - s4 +%= e; - s5 +%= f; - s6 +%= g; - s7 +%= h - }; - - public func writeIter(iter : { next() : ?Nat8 }) : () { - label reading loop { - switch (iter.next()) { - case (?val) { - writeByte(val); - continue reading - }; - case (null) { - break reading - } - } - } - }; - - public func writeArray(arr : [Nat8]) : () = writeIter(arr.vals()); - public func writeBlob(blob : Blob) : () = writeIter(blob.vals()); - - public func sum() : Blob { - // calculate padding - // t = bytes in the last incomplete block (0-63) - let t : Nat8 = (i_msg << 2) +% 4 -% i_byte; - // p = length of padding (1-64) - var p : Nat8 = if (t < 56) (56 -% t) else (120 -% t); - // n_bits = length of message in bits - let n_bits : Nat64 = ((i_block << 6) +% Nat64.fromIntWrap(Nat8.toNat(t))) << 3; - - // write padding - writeByte(0x80); - p -%= 1; - while (p != 0) { - writeByte(0x00); - p -%= 1 - }; - - // write length (8 bytes) - // Note: this exactly fills the block buffer, hence process_block will get - // triggered by the last writeByte - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 56) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 48) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 40) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 32) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 24) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 16) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 8) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat(n_bits & 0xff))); - - // retrieve sum - digest[0] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 24) & 0xff)); - digest[1] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 16) & 0xff)); - digest[2] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 8) & 0xff)); - digest[3] := Nat8.fromIntWrap(Nat32.toNat(s0 & 0xff)); - digest[4] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 24) & 0xff)); - digest[5] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 16) & 0xff)); - digest[6] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 8) & 0xff)); - digest[7] := Nat8.fromIntWrap(Nat32.toNat(s1 & 0xff)); - digest[8] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 24) & 0xff)); - digest[9] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 16) & 0xff)); - digest[10] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 8) & 0xff)); - digest[11] := Nat8.fromIntWrap(Nat32.toNat(s2 & 0xff)); - digest[12] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 24) & 0xff)); - digest[13] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 16) & 0xff)); - digest[14] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 8) & 0xff)); - digest[15] := Nat8.fromIntWrap(Nat32.toNat(s3 & 0xff)); - digest[16] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 24) & 0xff)); - digest[17] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 16) & 0xff)); - digest[18] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 8) & 0xff)); - digest[19] := Nat8.fromIntWrap(Nat32.toNat(s4 & 0xff)); - digest[20] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 24) & 0xff)); - digest[21] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 16) & 0xff)); - digest[22] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 8) & 0xff)); - digest[23] := Nat8.fromIntWrap(Nat32.toNat(s5 & 0xff)); - digest[24] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 24) & 0xff)); - digest[25] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 16) & 0xff)); - digest[26] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 8) & 0xff)); - digest[27] := Nat8.fromIntWrap(Nat32.toNat(s6 & 0xff)); - - return Blob.fromVarArray(digest) - } - }; // class SHA224 - - func nat32ToByteArray(n : Nat32) : [Nat8] { - func byte(n : Nat32) : Nat8 { - Nat8.fromNat(Nat32.toNat(n & 0xff)) - }; - [byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)] - }; - - func toByteArray(p : Principal) : [Nat8] = Blob.toArray(toBlob(p)); - - func isLastByte(byteArray : [Nat8], byte : Nat8) : Bool { - let size = byteArray.size(); - size > 0 and byteArray[size - 1] == byte - } -} diff --git a/.mops/core@2.4.0/src/PriorityQueue.mo b/.mops/core@2.4.0/src/PriorityQueue.mo deleted file mode 100644 index 4045b4f..0000000 --- a/.mops/core@2.4.0/src/PriorityQueue.mo +++ /dev/null @@ -1,299 +0,0 @@ -/// A mutable priority queue of elements. -/// Always returns the element with the highest priority first, -/// as determined by a user-provided comparison function. -/// -/// Typical use cases include: -/// * Task scheduling (highest-priority task first) -/// * Event simulation -/// * Pathfinding algorithms (e.g. Dijkstra, A*) -/// -/// Example: -/// ```motoko -/// import PriorityQueue "mo:core/PriorityQueue"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// let pq = PriorityQueue.empty(); -/// PriorityQueue.push(pq, Nat.compare, 5); -/// PriorityQueue.push(pq, Nat.compare, 10); -/// PriorityQueue.push(pq, Nat.compare, 3); -/// assert PriorityQueue.pop(pq, Nat.compare) == ?10; -/// assert PriorityQueue.pop(pq, Nat.compare) == ?5; -/// assert PriorityQueue.pop(pq, Nat.compare) == ?3; -/// assert PriorityQueue.pop(pq, Nat.compare) == null; -/// } -/// ``` -/// -/// Internally implemented as a binary heap stored in a core library `List`. -/// -/// Performance: -/// * Runtime: `O(log n)` for `push` and `pop` (amortized). -/// * Runtime: `O(1)` for `peek`, `clear`, `size`, and `isEmpty`. -/// * Space: `O(n)`, where `n` is the number of stored elements. -/// -/// Implementation note (due to `List`): -/// * There is an additive memory overhead of `O(sqrt(n))`. -/// * For `push` and `pop`, the amortized time is `O(log n)`, -/// but the worst case can involve an extra `O(sqrt(n))` step. -import List "List"; -import Types "Types"; -import Order "Order"; - -module { - public type PriorityQueue = Types.PriorityQueue; - - /// Returns an empty priority queue. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// - /// let pq = PriorityQueue.empty(); - /// assert PriorityQueue.isEmpty(pq); - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func empty() : PriorityQueue = { - heap = List.empty() - }; - - /// Returns a priority queue containing a single element. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// - /// let pq = PriorityQueue.singleton(42); - /// assert PriorityQueue.peek(pq) == ?42; - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func singleton(element : T) : PriorityQueue = { - heap = List.singleton(element) - }; - - /// Returns the number of elements in the priority queue. - /// - /// Runtime: `O(1)`. - public func size(self : PriorityQueue) : Nat = List.size(self.heap); - - /// Returns `true` iff the priority queue is empty. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let pq = PriorityQueue.empty(); - /// assert PriorityQueue.isEmpty(pq); - /// PriorityQueue.push(pq, Nat.compare, 5); - /// assert not PriorityQueue.isEmpty(pq); - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func isEmpty(self : PriorityQueue) : Bool = List.isEmpty(self.heap); - - /// Removes all elements from the priority queue. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// - /// let pq = PriorityQueue.empty(); - /// PriorityQueue.push(pq, Nat.compare, 5); - /// PriorityQueue.push(pq, Nat.compare, 10); - /// assert not PriorityQueue.isEmpty(pq); - /// PriorityQueue.clear(pq); - /// assert PriorityQueue.isEmpty(pq); - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func clear(self : PriorityQueue) = List.clear(self.heap); - - /// Inserts a new element into the priority queue. - /// - /// `compare` – comparison function that defines priority ordering. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let pq = PriorityQueue.empty(); - /// PriorityQueue.push(pq, Nat.compare, 5); - /// PriorityQueue.push(pq, Nat.compare, 10); - /// assert PriorityQueue.peek(pq) == ?10; - /// ``` - /// - /// Runtime: `O(log n)`. Space: `O(1)`. - public func push( - self : PriorityQueue, - compare : (implicit : (T, T) -> Order.Order), - element : T - ) { - let heap = self.heap; - List.add(heap, element); - var index : Nat = List.size(heap) - 1; - while (index > 0) { - let parentId = (index - 1) : Nat / 2; - let parentVal = List.at(heap, parentId); - if (compare(element, parentVal) == #greater) { - List.put(heap, index, parentVal); - index := parentId - } else { - List.put(heap, index, element); - return - } - }; - List.put(heap, 0, element) - }; - - /// Returns the element with the highest priority, without removing it. - /// Returns `null` if the queue is empty. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// - /// let pq = PriorityQueue.singleton(42); - /// assert PriorityQueue.peek(pq) == ?42; - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func peek(self : PriorityQueue) : ?T = List.get(self.heap, 0); - - /// Removes and returns the element with the highest priority. - /// Returns `null` if the queue is empty. - /// - /// `compare` – comparison function that defines priority ordering. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let pq = PriorityQueue.empty(); - /// PriorityQueue.push(pq, Nat.compare, 5); - /// PriorityQueue.push(pq, Nat.compare, 10); - /// assert PriorityQueue.pop(pq, Nat.compare) == ?10; - /// ``` - /// - /// Runtime: `O(log n)`. Space: `O(1)`. - public func pop( - self : PriorityQueue, - compare : (implicit : (T, T) -> Order.Order) - ) : ?T { - let heap = self.heap; - if (List.isEmpty(heap)) { - return null - }; - let top = List.get(heap, 0); - let lastIndex : Nat = List.size(heap) - 1; - let lastElem = List.at(heap, lastIndex); - - var index = 0; - loop { - var best = lastIndex; - let left = 2 * index + 1; - var bestElem = lastElem; - if (left < lastIndex) { - let leftElem = List.at(heap, left); - if (compare(leftElem, lastElem) == #greater) { - best := left; - bestElem := leftElem - } - }; - let right = left + 1; - if (right < lastIndex) { - let rightElem = List.at(heap, right); - if (compare(rightElem, bestElem) == #greater) { - best := right; - bestElem := rightElem - } - }; - if (best == lastIndex) { - List.put(heap, index, lastElem); - ignore List.removeLast(heap); - return top - }; - List.put(heap, index, bestElem); - index := best - } - }; - - /// Creates a new priority queue from an iterator. - /// - /// `compare` – comparison function that defines priority ordering. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let pq = PriorityQueue.fromIter([5, 10, 3].values(), Nat.compare); - /// assert PriorityQueue.size(pq) == 3; - /// assert PriorityQueue.peek(pq) == ?10; - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// `n` denotes the number of elements in the iterator. - public func fromIter(iter : Types.Iter, compare : (implicit : (T, T) -> Order.Order)) : PriorityQueue { - let pq = empty(); - for (element in iter) { - push(pq, element) - }; - pq - }; - - /// Creates a copy of the priority queue. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let original = PriorityQueue.fromIter([5, 10, 3].values(), Nat.compare); - /// let copy = PriorityQueue.clone(original); - /// assert PriorityQueue.pop(copy, Nat.compare) == ?10; - /// assert PriorityQueue.size(original) == 3; - /// ``` - /// - /// Runtime: `O(n)`. Space: `O(n)`. - /// `n` denotes the number of elements in the priority queue. - public func clone(self : PriorityQueue) : PriorityQueue = { - heap = List.clone(self.heap) - }; - - /// Returns an iterator that yields elements in descending priority order - /// (highest priority first, matching `pop` semantics). - /// - /// The original queue is not modified. Internally clones the heap - /// and pops from the clone on each `next()` call. - /// - /// `compare` – comparison function that defines priority ordering. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let pq = PriorityQueue.fromIter([5, 10, 3].values(), Nat.compare); - /// assert Iter.toArray(PriorityQueue.values(pq, Nat.compare)) == [10, 5, 3]; - /// ``` - /// - /// Runtime: `O(n)` to create the iterator, `O(log n)` per `next()` call. - /// Space: `O(n)` for the internal clone. - /// `n` denotes the number of elements in the priority queue. - public func values(self : PriorityQueue, compare : (implicit : (T, T) -> Order.Order)) : Types.Iter { - let copy : PriorityQueue = clone(self); - object { - public func next() : ?T { - pop(copy) - } - } - } -} diff --git a/.mops/core@2.4.0/src/Queue.mo b/.mops/core@2.4.0/src/Queue.mo deleted file mode 100644 index d8f48c5..0000000 --- a/.mops/core@2.4.0/src/Queue.mo +++ /dev/null @@ -1,820 +0,0 @@ -/// A mutable double-ended queue of elements. -/// The queue has two ends, front and back. -/// Elements can be added and removed at the two ends. -/// -/// This can be used for different use cases, such as: -/// * Queue (FIFO) by using `pushBack()` and `popFront()` -/// * Stack (LIFO) by using `pushFront()` and `popFront()`. -/// -/// Example: -/// ```motoko -/// import Queue "mo:core/Queue"; -/// -/// persistent actor { -/// let orders = Queue.empty(); -/// Queue.pushBack(orders, "Motoko"); -/// Queue.pushBack(orders, "Mops"); -/// Queue.pushBack(orders, "IC"); -/// assert Queue.popFront(orders) == ?"Motoko"; -/// assert Queue.popFront(orders) == ?"Mops"; -/// assert Queue.popFront(orders) == ?"IC"; -/// assert Queue.popFront(orders) == null; -/// } -/// ``` -/// -/// The internal implementation is a doubly-linked list. -/// -/// Performance: -/// * Runtime: `O(1)` for push, pop, and peek operations. -/// * Space: `O(n)`. -/// `n` denotes the number of elements stored in the queue. - -import PureQueue "pure/Queue"; -import Iter "Iter"; -import Order "Order"; -import Types "Types"; -import Array "Array"; -import Prim "mo:⛔"; - -module { - public type Queue = Types.Queue.Queue; - - type Node = Types.Queue.Node; - - /// Converts a mutable queue to an immutable, purely functional queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let pureQueue = Queue.toPure(queue); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - /// @deprecated M0235 - public func toPure(self : Queue) : PureQueue.Queue { - let pureQueue = PureQueue.empty(); - let iter = values(self); - var current = pureQueue; - loop { - switch (iter.next()) { - case null { return current }; - case (?val) { current := PureQueue.pushBack(current, val) } - } - } - }; - - /// Converts an immutable, purely functional queue to a mutable queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import PureQueue "mo:core/pure/Queue"; - /// - /// persistent actor { - /// let pureQueue = PureQueue.fromIter([1, 2, 3].values()); - /// let queue = Queue.fromPure(pureQueue); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - /// @deprecated M0235 - public func fromPure(pureQueue : PureQueue.Queue) : Queue { - let queue = empty(); - let iter = PureQueue.values(pureQueue); - loop { - switch (iter.next()) { - case null { return queue }; - case (?val) { pushBack(queue, val) } - } - } - }; - - /// Create a new empty mutable double-ended queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.size(queue) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Queue { - { var front = null; var back = null; var size = 0 } - }; - - /// Creates a new queue with a single element. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.singleton(123); - /// assert Queue.size(queue) == 1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func singleton(element : T) : Queue { - let queue = empty(); - pushBack(queue, element); - queue - }; - - /// Removes all elements from the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// Queue.clear(queue); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func clear(self : Queue) { - self.front := null; - self.back := null; - self.size := 0 - }; - - /// Creates a deep copy of the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let original = Queue.fromIter([1, 2, 3].values()); - /// let copy = Queue.clone(original); - /// Queue.clear(original); - /// assert Queue.size(original) == 0; - /// assert Queue.size(copy) == 3; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func clone(self : Queue) : Queue { - let copy = empty(); - for (element in values(self)) { - pushBack(copy, element) - }; - copy - }; - - /// Returns the number of elements in the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter(["A", "B", "C"].values()); - /// assert Queue.size(queue) == 3; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func size(self : Queue) : Nat { - self.size - }; - - /// Returns `true` if the queue contains no elements. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func isEmpty(self : Queue) : Bool { - self.size == 0 - }; - - /// Checks if an element exists in the queue using the provided equality function. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.contains(queue, Nat.equal, 2); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func contains(self : Queue, equal : (implicit : (T, T) -> Bool), element : T) : Bool { - for (existing in values(self)) { - if (equal(existing, element)) { - return true - } - }; - false - }; - - /// Returns the first element in the queue without removing it. - /// Returns null if the queue is empty. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.peekFront(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func peekFront(self : Queue) : ?T { - switch (self.front) { - case null null; - case (?node) ?node.value - } - }; - - /// Returns the last element in the queue without removing it. - /// Returns null if the queue is empty. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.peekBack(queue) == ?3; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func peekBack(self : Queue) : ?T { - switch (self.back) { - case null null; - case (?node) ?node.value - } - }; - - /// Adds an element to the front of the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.empty(); - /// Queue.pushFront(queue, 1); - /// assert Queue.peekFront(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func pushFront(self : Queue, element : T) { - let node : Node = { - value = element; - var next = self.front; - var previous = null - }; - switch (self.front) { - case null {}; - case (?first) first.previous := ?node - }; - self.front := ?node; - switch (self.back) { - case null self.back := ?node; - case (?_) {} - }; - self.size += 1 - }; - - /// Adds an element to the back of the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.empty(); - /// Queue.pushBack(queue, 1); - /// assert Queue.peekBack(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func pushBack(self : Queue, element : T) { - let node : Node = { - value = element; - var next = null; - var previous = self.back - }; - switch (self.back) { - case null {}; - case (?last) last.next := ?node - }; - self.back := ?node; - switch (self.front) { - case null self.front := ?node; - case (?_) {} - }; - self.size += 1 - }; - - /// Removes and returns the first element in the queue. - /// Returns null if the queue is empty. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.popFront(queue) == ?1; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func popFront(self : Queue) : ?T { - switch (self.front) { - case null null; - case (?first) { - self.front := first.next; - switch (self.front) { - case null { self.back := null }; - case (?newFirst) { newFirst.previous := null } - }; - self.size -= 1; - ?first.value - } - } - }; - - /// Removes and returns the last element in the queue. - /// Returns null if the queue is empty. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.popBack(queue) == ?3; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func popBack(self : Queue) : ?T { - switch (self.back) { - case null null; - case (?last) { - self.back := last.previous; - switch (self.back) { - case null { self.front := null }; - case (?newLast) { newLast.next := null } - }; - self.size -= 1; - ?last.value - } - } - }; - - /// Creates a new queue from an iterator. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter(["A", "B", "C"].values()); - /// assert Queue.size(queue) == 3; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func fromIter(iter : Iter.Iter) : Queue { - let queue = empty(); - for (element in iter) { - pushBack(queue, element) - }; - queue - }; - - /// Converts an iterator to a queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// transient let iter = ["A", "B", "C"].values(); - /// - /// let queue = iter.toQueue(); - /// - /// assert Queue.size(queue) == 3; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func toQueue(self : Iter.Iter) : Queue { - fromIter(self) - }; - - /// Creates a new queue from an array. - /// Elements appear in the same order as in the array. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromArray(["A", "B", "C"]); - /// assert Queue.size(queue) == 3; - /// assert Queue.peekFront(queue) == ?"A"; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the array. - public func fromArray(array : [T]) : Queue { - let queue = empty(); - for (element in array.vals()) { - pushBack(queue, element) - }; - queue - }; - - public func fromVarArray(array : [var T]) : Queue { - fromIter(array.values()) - }; - - /// Creates a new immutable array containing all elements from the queue. - /// Elements appear in the same order as in the queue (front to back). - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Array "mo:core/Array"; - /// - /// persistent actor { - /// let queue = Queue.fromArray(["A", "B", "C"]); - /// let array = Queue.toArray(queue); - /// assert array == ["A", "B", "C"]; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func toArray(self : Queue) : [T] { - let iter = values(self); - Array.tabulate( - self.size, - func(i) { - switch (iter.next()) { - case null { Prim.trap("Queue.toArray(): unexpected end of iterator") }; - case (?value) { value } - } - } - ) - }; - - public func toVarArray(self : Queue) : [var T] { - Array.toVarArray(toArray(self)) - }; - - /// Returns an iterator over the elements in the queue. - /// Iterates from front to back. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// persistent actor { - /// let queue = Queue.fromIter(["A", "B", "C"].values()); - /// transient let iter = Queue.values(queue); - /// assert iter.next() == ?"A"; - /// assert iter.next() == ?"B"; - /// assert iter.next() == ?"C"; - /// assert iter.next() == null; - /// } - /// ``` - /// - /// Runtime: O(1) for iterator creation, O(n) for full iteration - /// Space: O(1) - public func values(self : Queue) : Iter.Iter { - object { - var current = self.front; - - public func next() : ?T { - switch (current) { - case null null; - case (?node) { - current := node.next; - ?node.value - } - } - } - } - }; - - public func reverseValues(self : Queue) : Iter.Iter { - Iter.reverse(values(self)) - }; - - /// Tests whether all elements in the queue satisfy the given predicate. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([2, 4, 6].values()); - /// assert Queue.all(queue, func(x) { x % 2 == 0 }); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - public func all(self : Queue, predicate : T -> Bool) : Bool { - for (element in values(self)) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Tests whether any element in the queue satisfies the given predicate. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.any(queue, func (x) { x > 2 }); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func any(self : Queue, predicate : T -> Bool) : Bool { - for (element in values(self)) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Applies the given operation to all elements in the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// var sum = 0; - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// Queue.forEach(queue, func(x) { sum += x }); - /// assert sum == 6; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func forEach(self : Queue, operation : T -> ()) { - for (element in values(self)) { - operation(element) - } - }; - - /// Creates a new queue by applying the given function to all elements. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let doubled = Queue.map(queue, func(x) { x * 2 }); - /// assert Queue.peekFront(doubled) == ?2; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func map(self : Queue, project : T -> U) : Queue { - let result = empty(); - for (element in values(self)) { - pushBack(result, project(element)) - }; - result - }; - - /// Creates a new queue containing only elements that satisfy the given predicate. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3, 4].values()); - /// let evens = Queue.filter(queue, func(x) { x % 2 == 0 }); - /// assert Queue.size(evens) == 2; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func filter(self : Queue, criterion : T -> Bool) : Queue { - let result = empty(); - for (element in values(self)) { - if (criterion(element)) { - pushBack(result, element) - } - }; - result - }; - - /// Creates a new queue by applying the given function to all elements - /// and keeping only the non-null results. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3, 4].values()); - /// let evenDoubled = Queue.filterMap( - /// queue, - /// func(x) { - /// if (x % 2 == 0) { ?(x * 2) } else { null } - /// } - /// ); - /// assert Queue.size(evenDoubled) == 2; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func filterMap(self : Queue, project : T -> ?U) : Queue { - let result = empty(); - for (element in values(self)) { - switch (project(element)) { - case null {}; - case (?newElement) pushBack(result, newElement) - } - }; - result - }; - - /// Compares two queues for equality using the provided equality function. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2, 3].values()); - /// let queue2 = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.equal(queue1, queue2, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func equal(self : Queue, other : Queue, equal : (implicit : (T, T) -> Bool)) : Bool { - if (size(self) != size(other)) { - return false - }; - let iterator1 = values(self); - let iterator2 = values(other); - loop { - let element1 = iterator1.next(); - let element2 = iterator2.next(); - switch (element1, element2) { - case (null, null) { - return true - }; - case (?element1, ?element2) { - if (not equal(element1, element2)) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Converts a queue to its string representation using the provided element formatter. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.toText(queue, Nat.toText) == "Queue[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func toText(self : Queue, format : (implicit : (toText : T -> Text))) : Text { - var text = "Queue["; - var sep = ""; - for (element in values(self)) { - text #= sep # format(element); - sep := ", " - }; - text #= "]"; - text - }; - - /// Compares two queues using the provided comparison function. - /// Returns #less, #equal, or #greater. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2].values()); - /// let queue2 = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.compare(queue1, queue2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func compare(self : Queue, other : Queue, compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let iterator1 = values(self); - let iterator2 = values(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?element1, ?element2) { - let comparison = compare(element1, element2); - if (comparison != #equal) { - return comparison - } - } - } - } - } -} diff --git a/.mops/core@2.4.0/src/Random.mo b/.mops/core@2.4.0/src/Random.mo deleted file mode 100644 index 4283653..0000000 --- a/.mops/core@2.4.0/src/Random.mo +++ /dev/null @@ -1,456 +0,0 @@ -/// Random number generation. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Random "mo:core/Random"; -/// ``` - -import Nat8 "Nat8"; -import Nat64 "Nat64"; -import Int "Int"; -import Nat "Nat"; -import Blob "Blob"; -import Runtime "Runtime"; - -module { - - public type State = { - var bytes : [Nat8]; - var index : Nat; - var bits : Nat8; - var bitMask : Nat8 - }; - - public type SeedState = { - random : State; - prng : PRNG.State - }; - - let rawRand = (actor "aaaaa-aa" : actor { raw_rand : () -> async Blob }).raw_rand; - - public let blob : shared () -> async Blob = rawRand; - - public func bool() : async Bool { - await* crypto().bool() - }; - public func nat8() : async Nat8 { - await* crypto().nat8() - }; - public func nat64() : async Nat64 { - await* crypto().nat64() - }; - public func nat64Range(fromInclusive : Nat64, toExclusive : Nat64) : async Nat64 { - await* crypto().nat64Range(fromInclusive, toExclusive) - }; - public func natRange(fromInclusive : Nat, toExclusive : Nat) : async Nat { - await* crypto().natRange(fromInclusive, toExclusive) - }; - public func intRange(fromInclusive : Int, toExclusive : Int) : async Int { - await* crypto().intRange(fromInclusive, toExclusive) - }; - - /// Initializes a random number generator state. This is used - /// to create a `Random` or `AsyncRandom` instance with a specific state. - /// The state is empty, but it can be reused after upgrading the canister. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// let state = Random.emptyState(); - /// transient let random = Random.cryptoFromState(state); - /// - /// public func main() : async () { - /// let coin = await* random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func emptyState() : State = { - var bytes = []; - var index = 0; - var bits = 0x00; - var bitMask = 0x00 - }; - - /// Initializes a pseudo-random number generator state with a 64-bit seed. - /// This is used to create a `Random` instance with a specific seed. - /// The seed is used to initialize the PRNG state. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// let state = Random.seedState(123); - /// transient let random = Random.seedFromState(state); - /// - /// public func main() : async () { - /// let coin = random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func seedState(seed : Nat64) : SeedState = { - random = emptyState(); - prng = PRNG.init(seed) - }; - - /// Creates a pseudo-random number generator from a 64-bit seed. - /// The seed is used to initialize the PRNG state. - /// This is suitable for simulations and testing, but not for cryptographic purposes. - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(123); - /// let coin = random.bool(); // true or false - /// ``` - /// @deprecated M0235 - public func seed(seed : Nat64) : Random { - seedFromState(seedState(seed)) - }; - - /// Creates a pseudo-random number generator with the given state. - /// This provides statistical randomness suitable for simulations and testing, - /// but should not be used for cryptographic purposes. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// let state = Random.seedState(123); - /// transient let random = Random.seedFromState(state); - /// - /// public func main() : async () { - /// let coin = random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func seedFromState(state : SeedState) : Random { - Random( - state.random, - func() : Blob { - // Generate 8 bytes directly from a single 64-bit number - let n = PRNG.next(state.prng); - let (b7, b6, b5, b4, b3, b2, b1, b0) = Nat64.explode(n); - Blob.fromArray([b0, b1, b2, b3, b4, b5, b6, b7]) - } - ) - }; - - /// Initializes a cryptographic random number generator - /// using entropy from the ICP management canister. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// transient let random = Random.crypto(); - /// - /// public func main() : async () { - /// let coin = await* random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func crypto() : AsyncRandom { - cryptoFromState(emptyState()) - }; - - /// Creates a random number generator suitable for cryptography - /// using entropy from the ICP management canister. Initializing - /// from a state makes it possible to reuse entropy after - /// upgrading the canister. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// let state = Random.emptyState(); - /// transient let random = Random.cryptoFromState(state); - /// - /// func example() : async () { - /// let coin = await* random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func cryptoFromState(state : State) : AsyncRandom { - AsyncRandom(state, func() : async* Blob { await rawRand() }) - }; - - /// @deprecated M0235 - public class Random(state : State, generator : () -> Blob) { - - func nextBit() : Bool { - if (0 : Nat8 == state.bitMask) { - state.bits := nat8(); - state.bitMask := 0x40; - 0 : Nat8 != state.bits & (0x80 : Nat8) - } else { - let m = state.bitMask; - state.bitMask >>= (1 : Nat8); - 0 : Nat8 != state.bits & m - } - }; - - /// Random choice between `true` and `false`. - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let coin = random.bool(); // true or false - /// ``` - /// @deprecated M0235 - public func bool() : Bool { - nextBit() - }; - - /// Random `Nat8` value in the range [0, 256). - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let byte = random.nat8(); // 0 to 255 - /// ``` - /// @deprecated M0235 - public func nat8() : Nat8 { - if (state.index >= state.bytes.size()) { - let newBytes = Blob.toArray(generator()); - if (newBytes.size() == 0) { - Runtime.trap("Random: generator produced empty Blob") - }; - state.bytes := newBytes; - state.index := 0 - }; - let byte = state.bytes[state.index]; - state.index += 1; - byte - }; - - // Helper function which returns a uniformly sampled `Nat64` in the range `[0, max]`. - // Uses rejection sampling to ensure uniform distribution even when the range - // doesn't divide evenly into 2^64. This avoids modulo bias that would occur - // from simply taking the modulo of a random 64-bit number. - func uniform64(max : Nat64) : Nat64 { - if (max == 0) { - return 0 - }; - // if (max == 1) { - // return switch (bool()) { - // case false 0; - // case true 1 - // } - // }; - if (max == Nat64.maxValue) { - return nat64() - }; - let toExclusive = max + 1; - // 2^64 - (2^64 % toExclusive) = (2^64-1) - (2^64-1 % toExclusive): - let cutoff = Nat64.maxValue - (Nat64.maxValue % toExclusive); - // 2^64 / toExclusive, with toExclusive > 1: - let multiple = Nat64.fromNat(/* 2^64 */ 0x10000000000000000 / Nat64.toNat(toExclusive)); - loop { - // Build up a random Nat64 from bytes - var number = nat64(); - // If number is below cutoff, we can use it - if (number < cutoff) { - // Scale down to desired range - return number / multiple - }; - // Otherwise reject and try again - } - }; - - /// Random `Nat64` value in the range [0, 2^64). - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let number = random.nat64(); // 0 to 18446744073709551615 - /// ``` - /// @deprecated M0235 - public func nat64() : Nat64 { - (Nat64.fromNat(Nat8.toNat(nat8())) << 56) | (Nat64.fromNat(Nat8.toNat(nat8())) << 48) | (Nat64.fromNat(Nat8.toNat(nat8())) << 40) | (Nat64.fromNat(Nat8.toNat(nat8())) << 32) | (Nat64.fromNat(Nat8.toNat(nat8())) << 24) | (Nat64.fromNat(Nat8.toNat(nat8())) << 16) | (Nat64.fromNat(Nat8.toNat(nat8())) << 8) | Nat64.fromNat(Nat8.toNat(nat8())) - }; - - /// Random `Nat64` value in the range [fromInclusive, toExclusive). - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let dice = random.nat64Range(1, 7); // 1 to 6 - /// ``` - /// @deprecated M0235 - public func nat64Range(fromInclusive : Nat64, toExclusive : Nat64) : Nat64 { - if (fromInclusive >= toExclusive) { - Runtime.trap("Random.nat64Range(): fromInclusive >= toExclusive") - }; - uniform64(toExclusive - fromInclusive - 1) + fromInclusive - }; - - /// Random `Nat` value in the range [fromInclusive, toExclusive). - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let index = random.natRange(0, 10); // 0 to 9 - /// ``` - /// @deprecated M0235 - public func natRange(fromInclusive : Nat, toExclusive : Nat) : Nat { - if (fromInclusive >= toExclusive) { - Runtime.trap("Random.natRange(): fromInclusive >= toExclusive") - }; - Nat64.toNat(uniform64(Nat64.fromNat(toExclusive - fromInclusive - 1))) + fromInclusive - }; - - /// @deprecated M0235 - public func intRange(fromInclusive : Int, toExclusive : Int) : Int { - let range = Nat.fromInt(toExclusive - fromInclusive - 1); - Nat64.toNat(uniform64(Nat64.fromNat(range))) + fromInclusive - }; - - }; - - /// @deprecated M0235 - public class AsyncRandom(state : State, generator : () -> async* Blob) { - - func nextBit() : async* Bool { - if (0 : Nat8 == state.bitMask) { - state.bits := await* nat8(); - state.bitMask := 0x40; - 0 : Nat8 != state.bits & (0x80 : Nat8) - } else { - let m = state.bitMask; - state.bitMask >>= (1 : Nat8); - 0 : Nat8 != state.bits & m - } - }; - - /// Random choice between `true` and `false`. - /// @deprecated M0235 - public func bool() : async* Bool { - await* nextBit() - }; - - /// Random `Nat8` value in the range [0, 256). - /// @deprecated M0235 - public func nat8() : async* Nat8 { - if (state.index >= state.bytes.size()) { - let newBytes = Blob.toArray(await* generator()); - if (newBytes.size() == 0) { - Runtime.trap("AsyncRandom: generator produced empty Blob") - }; - state.bytes := newBytes; - state.index := 0 - }; - let byte = state.bytes[state.index]; - state.index += 1; - byte - }; - - // Helper function which returns a uniformly sampled `Nat64` in the range `[0, max]`. - // Uses rejection sampling to ensure uniform distribution even when the range - // doesn't divide evenly into 2^64. This avoids modulo bias that would occur - // from simply taking the modulo of a random 64-bit number. - func uniform64(max : Nat64) : async* Nat64 { - if (max == 0) { - return 0 - }; - if (max == Nat64.maxValue) { - return await* nat64() - }; - let toExclusive = max + 1; - // 2^64 - (2^64 % toExclusive) = (2^64-1) - (2^64-1 % toExclusive): - let cutoff = Nat64.maxValue - (Nat64.maxValue % toExclusive); - // 2^64 / toExclusive, with toExclusive > 1: - let multiple = Nat64.fromNat(/* 2^64 */ 0x10000000000000000 / Nat64.toNat(toExclusive)); - loop { - // Build up a random Nat64 from bytes - var number = await* nat64(); - // If number is below cutoff, we can use it - if (number < cutoff) { - // Scale down to desired range - return number / multiple - }; - // Otherwise reject and try again - } - }; - - /// Random `Nat64` value in the range [0, 2^64). - /// @deprecated M0235 - public func nat64() : async* Nat64 { - (Nat64.fromNat(Nat8.toNat(await* nat8())) << 56) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 48) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 40) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 32) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 24) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 16) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 8) | Nat64.fromNat(Nat8.toNat(await* nat8())) - }; - - /// Random `Nat64` value in the range [fromInclusive, toExclusive). - /// @deprecated M0235 - public func nat64Range(fromInclusive : Nat64, toExclusive : Nat64) : async* Nat64 { - if (fromInclusive >= toExclusive) { - Runtime.trap("AsyncRandom.nat64Range(): fromInclusive >= toExclusive") - }; - (await* uniform64(toExclusive - fromInclusive - 1)) + fromInclusive - }; - - /// Random `Nat` value in the range [fromInclusive, toExclusive). - /// @deprecated M0235 - public func natRange(fromInclusive : Nat, toExclusive : Nat) : async* Nat { - if (fromInclusive >= toExclusive) { - Runtime.trap("AsyncRandom.natRange(): fromInclusive >= toExclusive") - }; - Nat64.toNat(await* uniform64(Nat64.fromNat(toExclusive - fromInclusive - 1))) + fromInclusive - }; - - /// Random `Int` value in the range [fromInclusive, toExclusive). - /// @deprecated M0235 - public func intRange(fromInclusive : Int, toExclusive : Int) : async* Int { - let range = Nat.fromInt(toExclusive - fromInclusive - 1); - Nat64.toNat(await* uniform64(Nat64.fromNat(range))) + fromInclusive - }; - - }; - - // Derived from https://github.com/research-ag/prng - module PRNG { - let p : Nat64 = 24; - let q : Nat64 = 11; - let r : Nat64 = 3; - - public type State = { - var a : Nat64; - var b : Nat64; - var c : Nat64; - var d : Nat64 - }; - - public func init(seed : Nat64) : State { - init3(seed, seed, seed) - }; - - public func init3(seed1 : Nat64, seed2 : Nat64, seed3 : Nat64) : State { - let state : State = { - var a = seed1; - var b = seed2; - var c = seed3; - var d = 1 - }; - for (_ in Nat.range(0, 11)) ignore next(state); - state - }; - - public func next(state : State) : Nat64 { - let tmp = state.a +% state.b +% state.d; - state.a := state.b ^ (state.b >> q); - state.b := state.c +% (state.c << r); - state.c := (state.c <<> p) +% tmp; - state.d +%= 1; - tmp - } - } - -} diff --git a/.mops/core@2.4.0/src/Region.mo b/.mops/core@2.4.0/src/Region.mo deleted file mode 100644 index a08783a..0000000 --- a/.mops/core@2.4.0/src/Region.mo +++ /dev/null @@ -1,485 +0,0 @@ -/// Byte-level access to isolated, virtual stable memory regions. -/// -/// This is a moderately lightweight abstraction over IC _stable memory_ and supports persisting -/// regions of binary data across Motoko upgrades. -/// Use of this module is fully compatible with Motoko's use of -/// _stable variables_, whose persistence mechanism also uses (real) IC stable memory internally, but does not interfere with this API. -/// It is also fully compatible with existing uses of the `ExperimentalStableMemory` library, which has a similar interface, but, -/// only supported a single memory region, without isolation between different applications. -/// -/// The `Region` type is stable and can be used in stable data structures. -/// -/// A new, empty `Region` is allocated using function `new()`. -/// -/// Regions are stateful objects and can be distinguished by the numeric identifier returned by function `id(region)`. -/// Every region owns an initially empty, but growable sequence of virtual IC stable memory pages. -/// The current size, in pages, of a region is returned by function `size(region)`. -/// The size of a region determines the range, [ 0, ..., size(region)*2^16 ), of valid byte-offsets into the region; these offsets are used as the source and destination of `load`/`store` operations on the region. -/// -/// Memory is allocated to a region, using function `grow(region, pages)`, sequentially and on demand, in units of 64KiB logical pages, starting with 0 allocated pages. -/// A call to `grow` may succeed, returning the previous size of the region, or fail, returning a sentinel value. New pages are zero initialized. -/// -/// A size of a region can only grow and never shrink. -/// In addition, the stable memory pages allocated to a region will *not* be reclaimed by garbage collection, even -/// if the region object itself becomes unreachable. -/// -/// Growth is capped by a soft limit on physical page count controlled by compile-time flag -/// `--max-stable-pages ` (the default is 65536, or 4GiB). -/// -/// Each `load` operation loads from region relative byte address `offset` in little-endian -/// format using the natural bit-width of the type in question. -/// The operation traps if attempting to read beyond the current region size. -/// -/// Each `store` operation stores to region relative byte address `offset` in little-endian format using the natural bit-width of the type in question. -/// The operation traps if attempting to write beyond the current region size. -/// -/// Text values can be handled by using `Text.decodeUtf8` and `Text.encodeUtf8`, in conjunction with `loadBlob` and `storeBlob`. -/// -/// The current region allocation and region contents are preserved across upgrades. -/// -/// NB: The IC's actual stable memory size (`ic0.stable_size`) may exceed the -/// total page size reported by summing all regions sizes. -/// This (and the cap on growth) are to accommodate Motoko's stable variables and bookkeeping for regions. -/// Applications that plan to use Motoko stable variables sparingly or not at all can -/// increase `--max-stable-pages` as desired, approaching the IC maximum (initially 8GiB, then 32Gib, currently 64Gib). -/// All applications should reserve at least one page for stable variable data, even when no stable variables are used. -/// -/// Usage: -/// ```motoko no-repl name=import -/// import Region "mo:core/Region"; -/// ``` - -import Prim "mo:⛔"; - -module { - - /// A stateful handle to an isolated region of IC stable memory. - /// `Region` is a stable type and regions can be stored in stable variables. - /// @deprecated M0235 - public type Region = Prim.Types.Region; - - /// Allocate a new, isolated Region of size 0. - /// - /// Example: - /// - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// assert Region.size(region) == 0; - /// } - /// } - /// ``` - public let new : () -> Region = Prim.regionNew; - - /// Return a Nat identifying the given region. - /// May be used for equality, comparison and hashing. - /// NB: Regions returned by `new()` are numbered from 16 - /// (regions 0..15 are currently reserved for internal use). - /// Allocate a new, isolated Region of size 0. - /// - /// Example: - /// - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// assert Region.id(region) == 16; - /// } - /// } - /// ``` - public let id : (self : Region) -> Nat = Prim.regionId; - - /// Current size of `region`, in pages. - /// Each page is 64KiB (65536 bytes). - /// Initially `0`. - /// Preserved across upgrades, together with contents of allocated - /// stable memory. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let beforeSize = Region.size(region); - /// ignore Region.grow(region, 10); - /// let afterSize = Region.size(region); - /// assert afterSize - beforeSize == 10; - /// } - /// } - /// ``` - public let size : (self : Region) -> (pages : Nat64) = Prim.regionSize; - - /// Grow current `size` of `region` by the given number of pages. - /// Each page is 64KiB (65536 bytes). - /// Returns the previous `size` when able to grow. - /// Returns `0xFFFF_FFFF_FFFF_FFFF` if remaining pages insufficient. - /// Every new page is zero-initialized, containing byte 0x00 at every offset. - /// Function `grow` is capped by a soft limit on `size` controlled by compile-time flag - /// `--max-stable-pages ` (the default is 65536, or 4GiB). - /// - /// Example: - /// ```motoko no-repl include=import - /// import Error "mo:core/Error"; - /// - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let beforeSize = Region.grow(region, 10); - /// if (beforeSize == 0xFFFF_FFFF_FFFF_FFFF) { - /// throw Error.reject("Out of memory"); - /// }; - /// let afterSize = Region.size(region); - /// assert afterSize - beforeSize == 10; - /// } - /// } - /// ``` - public let grow : (self : Region, newPages : Nat64) -> (oldPages : Nat64) = Prim.regionGrow; - - /// Within `region`, load a `Nat8` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat8 = 123; - /// Region.storeNat8(region, offset, value); - /// assert Region.loadNat8(region, offset) == 123; - /// } - /// } - /// ``` - public let loadNat8 : (self : Region, offset : Nat64) -> Nat8 = Prim.regionLoadNat8; - - /// Within `region`, store a `Nat8` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat8 = 123; - /// Region.storeNat8(region, offset, value); - /// assert Region.loadNat8(region, offset) == 123; - /// } - /// } - /// ``` - public let storeNat8 : (self : Region, offset : Nat64, value : Nat8) -> () = Prim.regionStoreNat8; - - /// Within `region`, load a `Nat16` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat16 = 123; - /// Region.storeNat16(region, offset, value); - /// assert Region.loadNat16(region, offset) == 123; - /// } - /// } - /// ``` - public let loadNat16 : (self : Region, offset : Nat64) -> Nat16 = Prim.regionLoadNat16; - - /// Within `region`, store a `Nat16` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat16 = 123; - /// Region.storeNat16(region, offset, value); - /// assert Region.loadNat16(region, offset) == 123; - /// } - /// } - /// ``` - public let storeNat16 : (self : Region, offset : Nat64, value : Nat16) -> () = Prim.regionStoreNat16; - - /// Within `region`, load a `Nat32` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat32 = 123; - /// Region.storeNat32(region, offset, value); - /// assert Region.loadNat32(region, offset) == 123; - /// } - /// } - /// ``` - public let loadNat32 : (self : Region, offset : Nat64) -> Nat32 = Prim.regionLoadNat32; - - /// Within `region`, store a `Nat32` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat32 = 123; - /// Region.storeNat32(region, offset, value); - /// assert Region.loadNat32(region, offset) == 123; - /// } - /// } - /// ``` - public func storeNat32(self : Region, offset : Nat64, value : Nat32) : () = Prim.regionStoreNat32(self, offset, value); - - /// Within `region`, load a `Nat64` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat64 = 123; - /// Region.storeNat64(region, offset, value); - /// assert Region.loadNat64(region, offset) == 123; - /// } - /// } - /// ``` - public let loadNat64 : (self : Region, offset : Nat64) -> Nat64 = Prim.regionLoadNat64; - - /// Within `region`, store a `Nat64` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat64 = 123; - /// Region.storeNat64(region, offset, value); - /// assert Region.loadNat64(region, offset) == 123; - /// } - /// } - /// ``` - public let storeNat64 : (self : Region, offset : Nat64, value : Nat64) -> () = Prim.regionStoreNat64; - - /// Within `region`, load a `Int8` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int8 = 123; - /// Region.storeInt8(region, offset, value); - /// assert Region.loadInt8(region, offset) == 123; - /// } - /// } - /// ``` - public let loadInt8 : (self : Region, offset : Nat64) -> Int8 = Prim.regionLoadInt8; - - /// Within `region`, store a `Int8` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int8 = 123; - /// Region.storeInt8(region, offset, value); - /// assert Region.loadInt8(region, offset) == 123; - /// } - /// } - /// ``` - public let storeInt8 : (self : Region, offset : Nat64, value : Int8) -> () = Prim.regionStoreInt8; - - /// Within `region`, load a `Int16` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int16 = 123; - /// Region.storeInt16(region, offset, value); - /// assert Region.loadInt16(region, offset) == 123; - /// } - /// } - /// ``` - public let loadInt16 : (self : Region, offset : Nat64) -> Int16 = Prim.regionLoadInt16; - - /// Within `region`, store a `Int16` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int16 = 123; - /// Region.storeInt16(region, offset, value); - /// assert Region.loadInt16(region, offset) == 123; - /// } - /// } - /// ``` - public let storeInt16 : (self : Region, offset : Nat64, value : Int16) -> () = Prim.regionStoreInt16; - - /// Within `region`, load a `Int32` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int32 = 123; - /// Region.storeInt32(region, offset, value); - /// assert Region.loadInt32(region, offset) == 123; - /// } - /// } - /// ``` - public let loadInt32 : (self : Region, offset : Nat64) -> Int32 = Prim.regionLoadInt32; - - /// Within `region`, store a `Int32` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int32 = 123; - /// Region.storeInt32(region, offset, value); - /// assert Region.loadInt32(region, offset) == 123; - /// } - /// } - /// ``` - public let storeInt32 : (self : Region, offset : Nat64, value : Int32) -> () = Prim.regionStoreInt32; - - /// Within `region`, load a `Int64` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int64 = 123; - /// Region.storeInt64(region, offset, value); - /// assert Region.loadInt64(region, offset) == 123; - /// } - /// } - /// ``` - public let loadInt64 : (self : Region, offset : Nat64) -> Int64 = Prim.regionLoadInt64; - - /// Within `region`, store a `Int64` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int64 = 123; - /// Region.storeInt64(region, offset, value); - /// assert Region.loadInt64(region, offset) == 123; - /// } - /// } - /// ``` - public let storeInt64 : (self : Region, offset : Nat64, value : Int64) -> () = Prim.regionStoreInt64; - - /// Within `region`, loads a `Float` value from the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value = 1.25; - /// Region.storeFloat(region, offset, value); - /// assert Region.loadFloat(region, offset) == 1.25; - /// } - /// } - /// ``` - public let loadFloat : (self : Region, offset : Nat64) -> Float = Prim.regionLoadFloat; - - /// Within `region`, store float `value` at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value = 1.25; - /// Region.storeFloat(region, offset, value); - /// assert Region.loadFloat(region, offset) == 1.25; - /// } - /// } - /// ``` - public let storeFloat : (self : Region, offset : Nat64, value : Float) -> () = Prim.regionStoreFloat; - - /// Within `region,` load `size` bytes starting from `offset` as a `Blob`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// import Blob "mo:core/Blob"; - /// - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value = Blob.fromArray([1, 2, 3]); - /// let size = value.size(); - /// Region.storeBlob(region, offset, value); - /// assert Blob.toArray(Region.loadBlob(region, offset, size)) == [1, 2, 3]; - /// } - /// } - /// ``` - public let loadBlob : (self : Region, offset : Nat64, size : Nat) -> Blob = Prim.regionLoadBlob; - - /// Within `region, write `blob.size()` bytes of `blob` beginning at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// import Blob "mo:core/Blob"; - /// - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value = Blob.fromArray([1, 2, 3]); - /// let size = value.size(); - /// Region.storeBlob(region, offset, value); - /// assert Blob.toArray(Region.loadBlob(region, offset, size)) == [1, 2, 3]; - /// } - /// } - /// ``` - public let storeBlob : (self : Region, offset : Nat64, value : Blob) -> () = Prim.regionStoreBlob; - -} diff --git a/.mops/core@2.4.0/src/Result.mo b/.mops/core@2.4.0/src/Result.mo deleted file mode 100644 index 08aa478..0000000 --- a/.mops/core@2.4.0/src/Result.mo +++ /dev/null @@ -1,355 +0,0 @@ -/// Module for error handling with the Result type. -/// -/// The Result type is used for returning and propagating errors. It has two variants: -/// `#ok(Ok)`, representing success and containing a value, and `#err(Err)`, representing -/// error and containing an error value. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Result "mo:core/Result"; -/// ``` - -import Order "Order"; -import Types "Types"; - -module { - - /// The Result type used for returning and propagating errors. - /// - /// The simplest way of working with Results is to pattern match on them. - /// For example: - /// ```motoko include=import - /// import Text "mo:core/Text"; - /// - /// type Email = Text; - /// type ErrorMessage = Text; - /// - /// func validateEmail(email : Text) : Result.Result { - /// let parts = Text.split(email, #char '@'); - /// let beforeAt = parts.next(); - /// let afterAt = parts.next(); - /// switch (beforeAt, afterAt) { - /// case (?local, ?domain) { - /// if (local == "") return #err("Username cannot be empty"); - /// if (not Text.contains(domain, #char '.')) return #err("Invalid domain format"); - /// #ok(email) - /// }; - /// case _ #err("Email must contain exactly one @ symbol") - /// } - /// }; - /// - /// assert validateEmail("user@example.com") == #ok("user@example.com"); - /// assert validateEmail("invalid.email") == #err("Email must contain exactly one @ symbol"); - /// assert validateEmail("@domain.com") == #err("Username cannot be empty"); - /// assert validateEmail("user@invalid") == #err("Invalid domain format"); - /// ``` - /// @deprecated M0235 - public type Result = Types.Result; - - /// Compares two Results for equality. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let result1 = #ok 10; - /// let result2 = #ok 10; - /// let result3 = #err "error"; - /// - /// assert Result.equal(result1, result2, Nat.equal, Text.equal); - /// assert not Result.equal(result1, result3, Nat.equal, Text.equal); - /// ``` - public func equal( - self : Result, - other : Result, - equalOk : (implicit : (equal : Ok, Ok) -> Bool), - equalErr : (implicit : (equal : (Err, Err) -> Bool)) - ) : Bool { - switch (self, other) { - case (#ok(ok1), #ok(ok2)) { - equalOk(ok1, ok2) - }; - case (#err(err1), #err(err2)) { - equalErr(err1, err2) - }; - case _ { false } - } - }; - - /// Compares two Result values. `#ok` is larger than `#err`. This ordering is - /// arbitrary, but it lets you for example use Results as keys in ordered maps. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let result1 = #ok 5; - /// let result2 = #ok 10; - /// let result3 = #err "error"; - /// - /// assert Result.compare(result1, result2, Nat.compare, Text.compare) == #less; - /// assert Result.compare(result2, result1, Nat.compare, Text.compare) == #greater; - /// assert Result.compare(result1, result3, Nat.compare, Text.compare) == #greater; - /// ``` - public func compare( - self : Result, - other : Result, - compareOk : (implicit : (compare : (Ok, Ok) -> Order.Order)), - compareErr : (implicit : (compare : (Err, Err) -> Order.Order)) - ) : Order.Order { - switch (self, other) { - case (#ok(ok1), #ok(ok2)) { - compareOk(ok1, ok2) - }; - case (#err(err1), #err(err2)) { - compareErr(err1, err2) - }; - case (#ok(_), _) { #greater }; - case (#err(_), _) { #less } - } - }; - - /// Allows sequencing of Result values and functions that return - /// Results themselves. - /// ```motoko include=import - /// type Result = Result.Result; - /// func largerThan10(x : Nat) : Result = - /// if (x > 10) { #ok(x) } else { #err("Not larger than 10.") }; - /// - /// func smallerThan20(x : Nat) : Result = - /// if (x < 20) { #ok(x) } else { #err("Not smaller than 20.") }; - /// - /// func between10And20(x : Nat) : Result = - /// Result.chain(largerThan10(x), smallerThan20); - /// - /// assert between10And20(15) == #ok(15); - /// assert between10And20(9) == #err("Not larger than 10."); - /// assert between10And20(21) == #err("Not smaller than 20."); - /// ``` - public func chain( - self : Result, - f : Ok1 -> Result - ) : Result { - switch self { - case (#err(e)) { #err(e) }; - case (#ok(r)) { f(r) } - } - }; - - /// Flattens a nested Result. - /// - /// ```motoko include=import - /// assert Result.flatten(#ok(#ok(10))) == #ok(10); - /// assert Result.flatten(#err("Wrong")) == #err("Wrong"); - /// assert Result.flatten(#ok(#err("Wrong"))) == #err("Wrong"); - /// ``` - public func flatten( - self : Result, Err> - ) : Result { - switch self { - case (#ok(ok)) { ok }; - case (#err(err)) { #err(err) } - } - }; - - /// Maps the `Ok` type/value, leaving any `Err` type/value unchanged. - /// - /// Example: - /// ```motoko include=import - /// let result1 = #ok(42); - /// let result2 = #err("error"); - /// - /// let doubled1 = Result.mapOk(result1, func x = x * 2); - /// assert doubled1 == #ok(84); - /// - /// let doubled2 = Result.mapOk(result2, func x = x * 2); - /// assert doubled2 == #err("error"); - /// ``` - public func mapOk( - self : Result, - f : Ok1 -> Ok2 - ) : Result { - switch self { - case (#err(e)) { #err(e) }; - case (#ok(r)) { #ok(f(r)) } - } - }; - - /// Maps the `Err` type/value, leaving any `Ok` type/value unchanged. - /// - /// Example: - /// ```motoko include=import - /// let result1 = #ok(42); - /// let result2 = #err("error"); - /// - /// let mapped1 = Result.mapErr(result1, func x = x # "!"); - /// assert mapped1 == #ok(42); - /// - /// let mapped2 = Result.mapErr(result2, func x = x # "!"); - /// assert mapped2 == #err("error!"); - /// ``` - public func mapErr( - self : Result, - f : Err1 -> Err2 - ) : Result { - switch self { - case (#err(e)) { #err(f(e)) }; - case (#ok(r)) { #ok(r) } - } - }; - - /// Create a result from an option, including an error value to handle the `null` case. - /// ```motoko include=import - /// assert Result.fromOption(?42, "err") == #ok(42); - /// assert Result.fromOption(null, "err") == #err("err"); - /// ``` - public func fromOption(x : ?Ok, err : Err) : Result { - switch x { - case (?x) { #ok(x) }; - case null { #err(err) } - } - }; - - /// Create an option from a result, turning all #err into `null`. - /// ```motoko include=import - /// assert Result.toOption(#ok(42)) == ?42; - /// assert Result.toOption(#err("err")) == null; - /// ``` - public func toOption(self : Result) : ?Ok { - switch self { - case (#ok(x)) { ?x }; - case (#err(_)) { null } - } - }; - - /// Applies a function to a successful value and discards the result. Use - /// `forOk` if you're only interested in the side effect `f` produces. - /// - /// ```motoko include=import - /// var counter : Nat = 0; - /// Result.forOk(#ok(5), func (x : Nat) { counter += x }); - /// assert counter == 5; - /// Result.forOk(#err("Error"), func (x : Nat) { counter += x }); - /// assert counter == 5; - /// ``` - public func forOk(self : Result, f : Ok -> ()) { - switch self { - case (#ok(ok)) { f(ok) }; - case _ {} - } - }; - - /// Applies a function to an error value and discards the result. Use - /// `forErr` if you're only interested in the side effect `f` produces. - /// - /// ```motoko include=import - /// var counter : Nat = 0; - /// Result.forErr(#err("Error"), func (x : Text) { counter += 1 }); - /// assert counter == 1; - /// Result.forErr(#ok(5), func (x : Text) { counter += 1 }); - /// assert counter == 1; - /// ``` - public func forErr(self : Result, f : Err -> ()) { - switch self { - case (#err(err)) { f(err) }; - case _ {} - } - }; - - /// Whether this Result is an `#ok`. - /// - /// Example: - /// ```motoko include=import - /// assert Result.isOk(#ok(42)); - /// assert not Result.isOk(#err("error")); - /// ``` - public func isOk(self : Result) : Bool { - switch self { - case (#ok(_)) { true }; - case (#err(_)) { false } - } - }; - - /// Whether this Result is an `#err`. - /// - /// Example: - /// ```motoko include=import - /// assert Result.isErr(#err("error")); - /// assert not Result.isErr(#ok(42)); - /// ``` - public func isErr(self : Result) : Bool { - switch self { - case (#ok(_)) { false }; - case (#err(_)) { true } - } - }; - - /// Asserts that its argument is an `#ok` result, traps otherwise. - /// - /// Example: - /// ```motoko include=import - /// Result.assertOk(#ok(42)); // succeeds - /// // Result.assertOk(#err("error")); // would trap - /// ``` - public func assertOk(self : Result) { - switch self { - case (#err(_)) { assert false }; - case (#ok(_)) {} - } - }; - - /// Asserts that its argument is an `#err` result, traps otherwise. - /// - /// Example: - /// ```motoko include=import - /// Result.assertErr(#err("error")); // succeeds - /// // Result.assertErr(#ok(42)); // would trap - /// ``` - public func assertErr(self : Result) { - switch self { - case (#err(_)) {}; - case (#ok(_)) assert false - } - }; - - /// Converts an upper cased `#Ok`, `#Err` result type into a lowercased `#ok`, `#err` result type. - /// On the IC, a common convention is to use `#Ok` and `#Err` as the variants of a result type, - /// but in Motoko, we use `#ok` and `#err` instead. - /// - /// Example: - /// ```motoko include=import - /// let upper = #Ok(42); - /// let lower = Result.fromUpper(upper); - /// assert lower == #ok(42); - /// ``` - public func fromUpper( - result : { #Ok : Ok; #Err : Err } - ) : Result { - switch result { - case (#Ok(ok)) { #ok(ok) }; - case (#Err(err)) { #err(err) } - } - }; - - /// Converts a lower cased `#ok`, `#err` result type into an upper cased `#Ok`, `#Err` result type. - /// On the IC, a common convention is to use `#Ok` and `#Err` as the variants of a result type, - /// but in Motoko, we use `#ok` and `#err` instead. - /// - /// Example: - /// ```motoko include=import - /// let lower = #ok(42); - /// let upper = Result.toUpper(lower); - /// assert upper == #Ok(42); - /// ``` - public func toUpper( - self : Result - ) : { #Ok : Ok; #Err : Err } { - switch self { - case (#ok(ok)) { #Ok(ok) }; - case (#err(err)) { #Err(err) } - } - }; - -} diff --git a/.mops/core@2.4.0/src/Runtime.mo b/.mops/core@2.4.0/src/Runtime.mo deleted file mode 100644 index 4a797a1..0000000 --- a/.mops/core@2.4.0/src/Runtime.mo +++ /dev/null @@ -1,70 +0,0 @@ -/// Runtime utilities. -/// These functions were originally part of the `Debug` module. -/// -/// ```motoko name=import -/// import Runtime "mo:core/Runtime"; -/// ``` -import Prim "mo:⛔"; - -module { - - /// `trap(t)` traps execution with a user-provided diagnostic message. - /// - /// The caller of a future whose execution called `trap(t)` will - /// observe the trap as an `Error` value, thrown at `await`, with code - /// `#canister_error` and message `m`. Here `m` is a more descriptive `Text` - /// message derived from the provided `t`. See example for more details. - /// - /// NOTE: Other execution environments that cannot handle traps may only - /// propagate the trap and terminate execution, with or without some - /// descriptive message. - /// - /// ```motoko include=import no-validate - /// Runtime.trap("An error occurred!"); - /// ``` - public func trap(errorMessage : Text) : None { - Prim.trap errorMessage - }; - - /// `unreachable()` traps execution when code that should be unreachable is reached. - /// - /// This function is useful for marking code paths that should never be executed, - /// such as after exhaustive pattern matches or unreachable control flow branches. - /// If execution reaches this function, it indicates a programming error. - /// - /// ```motoko include=import no-validate - /// let number = switch (?5) { - /// case (?n) n; - /// case null Runtime.unreachable(); - /// }; - /// assert number == 5; - /// ``` - public func unreachable() : None { - trap("Runtime.unreachable()") - }; - - /// Returns the names of all canister environment variables. - /// - /// Example: - /// ```motoko include=import no-validate - /// let names = Runtime.envVarNames(); - /// ``` - public func envVarNames() : [Text] { - return Prim.envVarNames() - }; - - /// Returns an optional value of the canister environment variable with the given name. - /// - /// Example: - /// ```motoko include=import no-validate - /// let value = Runtime.envVar("MY_ENV_VAR"); - /// let result = switch (value) { - /// case (?v) v; - /// case null Runtime.trap("Unknown environment variable"); - /// }; - /// ``` - public func envVar(name : Text) : ?Text { - return Prim.envVar(name) - } - -} diff --git a/.mops/core@2.4.0/src/Set.mo b/.mops/core@2.4.0/src/Set.mo deleted file mode 100644 index 20ad4f5..0000000 --- a/.mops/core@2.4.0/src/Set.mo +++ /dev/null @@ -1,2756 +0,0 @@ -/// Imperative (mutable) sets based on order/comparison of elements. -/// A set is a collection of elements without duplicates. -/// The set data structure type is stable and can be used for orthogonal persistence. -/// -/// Example: -/// ```motoko -/// import Set "mo:core/Set"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// let set = Set.fromIter([3, 1, 2, 3].vals(), Nat.compare); -/// assert Set.size(set) == 3; -/// assert not Set.contains(set, Nat.compare, 4); -/// let diff = Set.difference(set, set, Nat.compare); -/// assert Set.isEmpty(diff); -/// } -/// ``` -/// -/// These sets are implemented as B-trees with order 32, a balanced search tree of ordered elements. -/// -/// Performance: -/// * Runtime: `O(log(n))` worst case cost per insertion, removal, and retrieval operation. -/// * Space: `O(n)` for storing the entire tree, -/// where `n` denotes the number of elements stored in the set. - -// Data structure implementation is courtesy of Byron Becker. -// Source: https://github.com/canscale/StableHeapBTreeMap -// Copyright (c) 2022 Byron Becker. -// Distributed under Apache 2.0 license. -// With adjustments by the Motoko team. - -import PureSet "pure/Set"; -import Types "Types"; -import Order "Order"; -import Array "Array"; -import VarArray "VarArray"; -import Runtime "Runtime"; -import Stack "Stack"; -import Option "Option"; -import Iter "Iter"; -import BTreeHelper "internal/BTreeHelper"; - -module { - let btreeOrder = 32; // Should be >= 4 and <= 512. - - public type Set = Types.Set.Set; - type Node = Types.Set.Node; - type Data = Types.Set.Data; - type Internal = Types.Set.Internal; - type Leaf = Types.Set.Leaf; - - /// Convert the mutable set to an immutable, purely functional set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import PureSet "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 1].values(), Nat.compare); - /// let pureSet = Set.toPure(set, Nat.compare); - /// assert Iter.toArray(PureSet.values(pureSet)) == Iter.toArray(Set.values(set)); - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - /// @deprecated M0235 - public func toPure(self : Set, compare : (implicit : (T, T) -> Order.Order)) : PureSet.Set { - PureSet.fromIter(values(self), compare) - }; - - /// Convert an immutable, purely functional set to a mutable set. - /// - /// Example: - /// ```motoko - /// import PureSet "mo:core/pure/Set"; - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let pureSet = PureSet.fromIter([3, 1, 2].values(), Nat.compare); - /// let set = Set.fromPure(pureSet, Nat.compare); - /// assert Iter.toArray(Set.values(set)) == Iter.toArray(PureSet.values(pureSet)); - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func fromPure(set : PureSet.Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - fromIter(PureSet.values(set), compare) - }; - - public func fromArray(array : [T], compare : (implicit : (T, T) -> Order.Order)) : Set { - fromIter(array.values(), compare) - }; - - /// Create a copy of the mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let originalSet = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let clonedSet = Set.clone(originalSet); - /// Set.add(originalSet, Nat.compare, 4); - /// assert Set.size(clonedSet) == 3; - /// assert Set.size(originalSet) == 4; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the set. - public func clone(self : Set) : Set { - { - var root = cloneNode(self.root); - var size = self.size - } - }; - - /// Create a new empty mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Set.size(set) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Set { - { - var root = #leaf({ - data = { - elements = VarArray.repeat(null, btreeOrder - 1); - var count = 0 - } - }); - var size = 0 - } - }; - - /// Create a new mutable set with a single element. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// - /// persistent actor { - /// let cities = Set.singleton("Zurich"); - /// assert Set.size(cities) == 1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func singleton(element : T) : Set { - let elements = VarArray.repeat(null, btreeOrder - 1); - elements[0] := ?element; - { - var root = - #leaf({ data = { elements; var count = 1 } }); - var size = 1 - } - }; - - /// Remove all the elements from the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let cities = Set.empty(); - /// Set.add(cities, Text.compare, "Zurich"); - /// Set.add(cities, Text.compare, "San Francisco"); - /// Set.add(cities, Text.compare, "London"); - /// assert Set.size(cities) == 3; - /// - /// Set.clear(cities); - /// assert Set.size(cities) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func clear(self : Set) { - let emptySet = empty(); - self.root := emptySet.root; - self.size := 0 - }; - - /// Determines whether a set is empty. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 3); - /// - /// assert not Set.isEmpty(set); - /// Set.clear(set); - /// assert Set.isEmpty(set); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func isEmpty(self : Set) : Bool { - self.size == 0 - }; - - /// Return the number of elements in a set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 3); - /// - /// assert Set.size(set) == 3; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func size(self : Set) : Nat { - self.size - }; - - /// Test whether two imperative sets are equal. - /// Both sets have to be constructed by the same comparison function. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([2, 1].values(), Nat.compare); - /// let set3 = Set.fromIter([2, 1, 0].values(), Nat.compare); - /// assert Set.equal(set1, set2, Nat.compare); - /// assert not Set.equal(set1, set3, Nat.compare); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func equal(self : Set, other : Set, compare : (implicit : (T, T) -> Types.Order)) : Bool { - if (self.size != other.size) return false; - // TODO: optimize - let iterator1 = values(self); - let iterator2 = values(other); - loop { - let next1 = iterator1.next(); - let next2 = iterator2.next(); - switch (next1, next2) { - case (null, null) { - return true - }; - case (?element1, ?element2) { - if (not (compare(element1, element2) == #equal)) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Tests whether the set contains the provided element. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 3); - /// - /// assert Set.contains(set, Nat.compare, 1); - /// assert not Set.contains(set, Nat.compare, 4); - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func contains(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Bool { - switch (self.root) { - case (#internal(internalNode)) { - containsInInternal(internalNode, compare, element) - }; - case (#leaf(leafNode)) { containsInLeaf(leafNode, compare, element) } - } - }; - - /// Add a new element to a set. - /// No effect if the element already exists in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// assert Iter.toArray(Set.values(set)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func add(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) { - ignore insert(self, compare, element) - }; - - /// Insert a new element in the set. - /// Returns true if the element is new, false if the element was already contained in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Set.insert(set, Nat.compare, 2); - /// assert Set.insert(set, Nat.compare, 1); - /// assert not Set.insert(set, Nat.compare, 2); - /// assert Iter.toArray(Set.values(set)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func insert(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Bool { - let insertResult = switch (self.root) { - case (#leaf(leafNode)) { - leafInsertHelper(leafNode, btreeOrder, compare, element) - }; - case (#internal(internalNode)) { - internalInsertHelper(internalNode, btreeOrder, compare, element) - } - }; - - switch (insertResult) { - case (#inserted) { - // if inserted an element that was not previously there, increment the tree size counter - self.size += 1; - true - }; - case (#existent) { - // keep size - false - }; - case (#promote({ element = promotedElement; leftChild; rightChild })) { - let elements = VarArray.repeat(null, btreeOrder - 1); - elements[0] := ?promotedElement; - let children = VarArray.repeat>(null, btreeOrder); - children[0] := ?leftChild; - children[1] := ?rightChild; - self.root := #internal({ - data = { elements; var count = 1 }; - children - }); - // promotion always comes from inserting a new element, so increment the tree size counter - self.size += 1; - true - } - } - }; - - /// Deletes an element from a set. - /// No effect if the element is not contained in the set. - /// - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// - /// Set.remove(set, Nat.compare, 2); - /// assert not Set.contains(set, Nat.compare, 2); - /// - /// Set.remove(set, Nat.compare, 4); - /// assert not Set.contains(set, Nat.compare, 4); - /// - /// assert Iter.toArray(Set.values(set)) == [1, 3]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - public func remove(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : () { - ignore delete(self, compare, element) - }; - - /// Deletes an element from a set. - /// Returns true if the element was contained in the set, false if not. - /// - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// - /// assert Set.delete(set, Nat.compare, 2); - /// assert not Set.contains(set, Nat.compare, 2); - /// - /// assert not Set.delete(set, Nat.compare, 4); - /// assert not Set.contains(set, Nat.compare, 4); - /// assert Iter.toArray(Set.values(set)) == [1, 3]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// @deprecated M0235 - public func delete(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Bool { - let deleted = switch (self.root) { - case (#leaf(leafNode)) { - // TODO: think about how this can be optimized so don't have to do two steps (search and then insert)? - switch (NodeUtil.getElementIndex(leafNode.data, compare, element)) { - case (#elementFound(deleteIndex)) { - leafNode.data.count -= 1; - ignore BTreeHelper.deleteAndShift(leafNode.data.elements, deleteIndex); - self.size -= 1; - true - }; - case _ { false } - } - }; - case (#internal(internalNode)) { - let deletedElement = switch (internalDeleteHelper(internalNode, btreeOrder, compare, element, false)) { - case (#deleted) { true }; - case (#inexistent) { false }; - case (#mergeChild({ internalChild })) { - if (internalChild.data.count > 0) { - self.root := #internal(internalChild) - } - // This case will be hit if the BTree has order == 4 - // In this case, the internalChild has no element (last element was merged with new child), so need to promote that merged child (its only child) - else { - self.root := switch (internalChild.children[0]) { - case (?node) { node }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.delete(), element deletion failed, due to a null replacement node error") - } - } - }; - true - } - }; - if (deletedElement) { - // if deleted an element from the BTree, decrement the size - self.size -= 1 - }; - deletedElement - } - }; - deleted - }; - - /// Retrieves the maximum element from the set. - /// If the set is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Set.max(set) == null; - /// Set.add(set, Nat.compare, 3); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// assert Set.max(set) == ?3; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set. - public func max(self : Set) : ?T { - reverseValues(self).next() - }; - - /// Retrieves the minimum element from the set. - /// If the set is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Set.min(set) == null; - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 3); - /// assert Set.min(set) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set. - public func min(self : Set) : ?T { - values(self).next() - }; - - public func toArray(self : Set) : [T] { - Iter.toArray(values(self)) - }; - - /// Returns an iterator over the elements in the set, - /// traversing the elements in the ascending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 3, 1].values(), Nat.compare); - /// - /// var tmp = ""; - /// for (number in Set.values(set)) { - /// tmp #= " " # Nat.toText(number); - /// }; - /// assert tmp == " 0 1 2 3"; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func values(self : Set) : Types.Iter { - switch (self.root) { - case (#leaf(leafNode)) { return leafElements(leafNode) }; - case (#internal(internalNode)) { internalElements(internalNode) } - } - }; - - /// Returns an iterator over the elements in the set, - /// starting from a given element in ascending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1].values(), Nat.compare); - /// assert Iter.toArray(Set.valuesFrom(set, Nat.compare, 1)) == [1, 3]; - /// assert Iter.toArray(Set.valuesFrom(set, Nat.compare, 2)) == [3]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func valuesFrom( - self : Set, - compare : (implicit : (T, T) -> Order.Order), - element : T - ) : Types.Iter { - switch (self.root) { - case (#leaf(leafNode)) leafElementsFrom(leafNode, compare, element); - case (#internal(internalNode)) internalElementsFrom(internalNode, compare, element) - } - }; - - /// Returns an iterator over the elements in the set, - /// traversing the elements in the descending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 3, 1].values(), Nat.compare); - /// - /// var tmp = ""; - /// for (number in Set.reverseValues(set)) { - /// tmp #= " " # Nat.toText(number); - /// }; - /// assert tmp == " 3 2 1 0"; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseValues(self : Set) : Types.Iter { - switch (self.root) { - case (#leaf(leafNode)) { return reverseLeafElements(leafNode) }; - case (#internal(internalNode)) { reverseInternalElements(internalNode) } - } - }; - - /// Returns an iterator over the elements in the set, - /// starting from a given element in descending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 1, 3].values(), Nat.compare); - /// assert Iter.toArray(Set.reverseValuesFrom(set, Nat.compare, 0)) == [0]; - /// assert Iter.toArray(Set.reverseValuesFrom(set, Nat.compare, 2)) == [1, 0]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseValuesFrom( - self : Set, - compare : (implicit : (T, T) -> Order.Order), - element : T - ) : Types.Iter { - switch (self.root) { - case (#leaf(leafNode)) reverseLeafElementsFrom(leafNode, compare, element); - case (#internal(internalNode)) reverseInternalElementsFrom(internalNode, compare, element) - } - }; - - /// Create a mutable set with the elements obtained from an iterator. - /// Potential duplicate elements in the iterator are ignored, i.e. - /// multiple occurrence of an equal element only occur once in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([3, 1, 2, 1].values(), Nat.compare); - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func fromIter(iter : Types.Iter, compare : (implicit : (T, T) -> Order.Order)) : Set { - let set = empty(); - for (element in iter) { - add(set, compare, element) - }; - set - }; - - /// Convert an iterator of elements to a mutable set. - /// Potential duplicate elements in the iterator are ignored, i.e. - /// multiple occurrence of an equal element only occur once in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = [3, 1, 2, 1].values(); - /// - /// let set = iter.toSet(Nat.compare); - /// - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func toSet(self : Types.Iter, compare : (implicit : (T, T) -> Order.Order)) : Set { - fromIter(self, compare) - }; - - /// Test whether `set1` is a sub-set of `set2`, i.e. each element in `set1` is - /// also contained in `set2`. Returns `true` if both sets are equal. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([2, 1, 0].values(), Nat.compare); - /// let set3 = Set.fromIter([3, 4].values(), Nat.compare); - /// assert Set.isSubset(set1, set2, Nat.compare); - /// assert not Set.isSubset(set1, set3, Nat.compare); - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func isSubset(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Bool { - if (self.size > other.size) { return false }; - // TODO: optimize - for (element in values(self)) { - if (not contains(other, compare, element)) { - return false - } - }; - true - }; - - /// Returns a new set that is the union of `set1` and `set2`, - /// i.e. a new set that all the elements that exist in at least on of the two sets. - /// Potential duplicates are ignored, i.e. if the same element occurs in both `set1` - /// and `set2`, it only occurs once in the returned set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let union = Set.union(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(union)) == [1, 2, 3, 4, 5]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func union(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = clone(self); - for (element in values(other)) { - if (not contains(result, compare, element)) { - add(result, compare, element) - } - }; - result - }; - - /// Returns a new set that is the intersection of `set1` and `set2`, - /// i.e. a new set that contains all the elements that exist in both sets. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let intersection = Set.intersection(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(intersection)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func intersection(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = empty(); - for (element in values(self)) { - if (contains(other, compare, element)) { - add(result, compare, element) - } - }; - result - }; - - /// Returns a new set that is the difference between `set1` and `set2` (`set1` minus `set2`), - /// i.e. a new set that contains all the elements of `set1` that do not exist in `set2`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let difference = Set.difference(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(difference)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func difference(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = empty(); - for (element in values(self)) { - if (not contains(other, compare, element)) { - add(result, compare, element) - } - }; - result - }; - - /// Adds all elements from `iter` to the specified `set`. - /// This is equivalent to `Set.union()` but modifies the set in place. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// Set.addAll(set, Nat.compare, [3, 4, 5].values()); - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3, 4, 5]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements in `set` and `iter`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func addAll(self : Set, compare : (implicit : (T, T) -> Order.Order), iter : Types.Iter) { - for (element in iter) { - add(self, compare, element) - } - }; - - /// Deletes all values in `iter` from the specified `set`. - /// Returns `true` if any value was present in the set, otherwise false. - /// The return value indicates whether the size of the set has changed. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 1, 2].values(), Nat.compare); - /// assert Set.deleteAll(set, Nat.compare, [0, 2].values()); - /// assert Iter.toArray(Set.values(set)) == [1]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements in `set` and `iter`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func deleteAll(self : Set, compare : (implicit : (T, T) -> Order.Order), iter : Types.Iter) : Bool { - var deleted = false; - for (element in iter) { - deleted := delete(self, compare, element) or deleted // order matters! - }; - deleted - }; - - /// Inserts all values in `iter` into `set`. - /// Returns true if any value was not contained in the original set, otherwise false. - /// The return value indicates whether the size of the set has changed. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 1, 2].values(), Nat.compare); - /// assert Set.insertAll(set, Nat.compare, [0, 2, 3].values()); - /// assert Iter.toArray(Set.values(set)) == [0, 1, 2, 3]; - /// assert not Set.insertAll(set, Nat.compare, [0, 1, 2].values()); // no change - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements in `set` and `iter`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func insertAll(self : Set, compare : (implicit : (T, T) -> Order.Order), iter : Types.Iter) : Bool { - var inserted = false; - for (element in iter) { - inserted := insert(self, compare, element) or inserted // order matters! - }; - inserted - }; - - /// Removes all values in `set` that do not satisfy the given predicate. - /// Returns `true` if and only if the size of the set has changed. - /// Modifies the set in place. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([3, 1, 2].values(), Nat.compare); - /// - /// let sizeChanged = Set.retainAll(set, Nat.compare, func n { n % 2 == 0 }); - /// assert Iter.toArray(Set.values(set)) == [2]; - /// assert sizeChanged; - /// } - /// ``` - public func retainAll(self : Set, compare : (implicit : (T, T) -> Order.Order), predicate : T -> Bool) : Bool { - let array = Array.fromIter(values(self)); - deleteAll( - self, - compare, - Iter.filter(array.vals(), func(element : T) : Bool = not predicate(element)) - ) - }; - - /// Apply an operation on each element contained in the set. - /// The operation is applied in ascending order of the elements. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// var tmp = ""; - /// Set.forEach(numbers, func (element) { - /// tmp #= " " # Nat.toText(element) - /// }); - /// assert tmp == " 0 1 2 3"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func forEach(self : Set, operation : T -> ()) { - for (element in values(self)) { - operation(element) - } - }; - - /// Filter elements in a new set. - /// Create a copy of the mutable set that only contains the elements - /// that fulfil the criterion function. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let evenNumbers = Set.filter(numbers, Nat.compare, func (number) { - /// number % 2 == 0 - /// }); - /// assert Iter.toArray(Set.values(evenNumbers)) == [0, 2]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func filter(self : Set, compare : (implicit : (T, T) -> Order.Order), criterion : T -> Bool) : Set { - let result = empty(); - for (element in values(self)) { - if (criterion(element)) { - add(result, compare, element) - } - }; - result - }; - - /// Project all elements of the set in a new set. - /// Apply a mapping function to each element in the set and - /// collect the mapped elements in a new mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([3, 1, 2].values(), Nat.compare); - /// - /// let textNumbers = - /// Set.map(numbers, Text.compare, Nat.toText); - /// assert Iter.toArray(Set.values(textNumbers)) == ["1", "2", "3"]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func map(self : Set, compare : (implicit : (T2, T2) -> Order.Order), project : T1 -> T2) : Set { - let result = empty(); - for (element1 in values(self)) { - let element2 = project(element1); - add(result, compare, element2) - }; - result - }; - - /// Filter all elements in the set by also applying a projection to the elements. - /// Apply a mapping function `project` to all elements in the set and collect all - /// elements, for which the function returns a non-null new element. Collect all - /// non-discarded new elements in a new mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([3, 0, 2, 1].values(), Nat.compare); - /// - /// let evenTextNumbers = Set.filterMap(numbers, Text.compare, func (number) { - /// if (number % 2 == 0) { - /// ?Nat.toText(number) - /// } else { - /// null // discard odd numbers - /// } - /// }); - /// assert Iter.toArray(Set.values(evenTextNumbers)) == ["0", "2"]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func filterMap(self : Set, compare : (implicit : (T2, T2) -> Order.Order), project : T1 -> ?T2) : Set { - let result = empty(); - for (element1 in values(self)) { - switch (project(element1)) { - case null {}; - case (?element2) add(result, compare, element2) - } - }; - result - }; - - /// Iterate all elements in ascending order, - /// and accumulate the elements by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1].values(), Nat.compare); - /// - /// let text = Set.foldLeft( - /// set, - /// "", - /// func (accumulator, element) { - /// accumulator # " " # Nat.toText(element) - /// } - /// ); - /// assert text == " 0 1 2 3"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func foldLeft( - self : Set, - base : A, - combine : (A, T) -> A - ) : A { - var accumulator = base; - for (element in values(self)) { - accumulator := combine(accumulator, element) - }; - accumulator - }; - - /// Iterate all elements in descending order, - /// and accumulate the elements by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1].values(), Nat.compare); - /// - /// let text = Set.foldRight( - /// set, - /// "", - /// func (element, accumulator) { - /// accumulator # " " # Nat.toText(element) - /// } - /// ); - /// assert text == " 3 2 1 0"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func foldRight( - self : Set, - base : A, - combine : (T, A) -> A - ) : A { - var accumulator = base; - for (element in reverseValues(self)) { - accumulator := combine(element, accumulator) - }; - accumulator - }; - - /// Construct the union of a series of sets, i.e. all elements of - /// each set are included in the result set. - /// Any duplicates are ignored, i.e. if an element occurs - /// in several of the iterated sets, it only occurs once in the result set. - /// - /// Assumes all sets are ordered by `compare`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let set3 = Set.fromIter([5, 6, 7].values(), Nat.compare); - /// let combined = Set.join([set1, set2, set3].values(), Nat.compare); - /// assert Iter.toArray(Set.values(combined)) == [1, 2, 3, 4, 5, 6, 7]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the iterated sets, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func join(setIterator : Types.Iter>, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = empty(); - for (set in setIterator) { - for (element in values(set)) { - add(result, compare, element) - } - }; - result - }; - - /// Construct the union of a set of element sets, i.e. all elements of - /// each element set are included in the result set. - /// Any duplicates are ignored, i.e. if the same element occurs in multiple element sets, - /// it only occurs once in the result set. - /// - /// Assumes all sets are ordered by `compare`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Order "mo:core/Order"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// func setCompare(first: Set.Set, second: Set.Set) : Order.Order { - /// Set.compare(first, second, Nat.compare) - /// }; - /// - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let set3 = Set.fromIter([5, 6, 7].values(), Nat.compare); - /// let setOfSets = Set.fromIter([set1, set2, set3].values(), setCompare); - /// let flatSet = Set.flatten(setOfSets, Nat.compare); - /// assert Iter.toArray(Set.values(flatSet)) == [1, 2, 3, 4, 5, 6, 7]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in all the sub-sets, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func flatten(self : Set>, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = empty(); - for (subSet in values(self)) { - for (element in values(subSet)) { - add(result, compare, element) - } - }; - result - }; - - /// Check whether all elements in the set satisfy a predicate, i.e. - /// the `predicate` function returns `true` for all elements in the set. - /// Returns `true` for an empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let belowTen = Set.all(set, func (number) { - /// number < 10 - /// }); - /// assert belowTen; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func all(self : Set, predicate : T -> Bool) : Bool { - // TODO optimize, avoiding iterator - for (element in values(self)) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Check whether at least one element in the set satisfies a predicate, i.e. - /// the `predicate` function returns `true` for at least one element in the set. - /// Returns `false` for an empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let aboveTen = Set.any(set, func (number) { - /// number > 10 - /// }); - /// assert not aboveTen; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func any(self : Set, predicate : T -> Bool) : Bool { - // TODO optimize, avoiding iterator - for (element in values(self)) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Internal sanity check function. - /// Can be used to check that elements have been inserted with a consistent comparison function. - /// Traps if the internal set structure is invalid. - /// @deprecated M0235 - public func assertValid(self : Set, compare : (implicit : (T, T) -> Order.Order)) { - func checkIteration(iterator : Types.Iter, order : Order.Order) { - switch (iterator.next()) { - case null {}; - case (?first) { - var previous = first; - loop { - switch (iterator.next()) { - case null return; - case (?next) { - if (compare(previous, next) != order) { - Runtime.trap("Invalid order") - }; - previous := next - } - } - } - } - } - }; - checkIteration(values(self), #less); - checkIteration(reverseValues(self), #greater) - }; - - /// Generate a textual representation of all the elements in the set. - /// Primarily to be used for testing and debugging. - /// The elements are formatted according to `elementFormat`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// assert Set.toText(set, Nat.toText) == "Set{0, 1, 2, 3}" - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that `elementFormat` has runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func toText(self : Set, toText : (implicit : T -> Text)) : Text { - var text = "Set{"; - var sep = ""; - for (element in values(self)) { - text #= sep # toText(element); - sep := ", " - }; - text # "}" - }; - - /// Compare two sets by comparing the elements. - /// Both sets must have been created by the same comparison function. - /// The two sets are iterated by the ascending order of their creation and - /// order is determined by the following rules: - /// Less: - /// `set1` is less than `set2` if: - /// * the pairwise iteration hits an element pair `element1` and `element2` where - /// `element1` is less than `element2` and all preceding elements are equal, or, - /// * `set1` is a strict prefix of `set2`, i.e. `set2` has more elements than `set1` - /// and all elements of `set1` occur at the beginning of iteration `set2`. - /// Equal: - /// `set1` and `set2` have same series of equal elements by pairwise iteration. - /// Greater: - /// `set1` is neither less nor equal `set2`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 1].values(), Nat.compare); - /// let set2 = Set.fromIter([0, 2].values(), Nat.compare); - /// - /// assert Set.compare(set1, set2, Nat.compare) == #less; - /// assert Set.compare(set1, set1, Nat.compare) == #equal; - /// assert Set.compare(set2, set1, Nat.compare) == #greater; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that `compare` has runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func compare(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let iterator1 = values(self); - let iterator2 = values(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?element1, ?element2) { - let comparison = compare(element1, element2); - if (comparison != #equal) { - return comparison - } - } - } - } - }; - - func leafElements({ data } : Leaf) : Types.Iter { - var i : Nat = 0; - object { - public func next() : ?T { - if (i >= data.count) { - null - } else { - let res = data.elements[i]; - i += 1; - res - } - } - } - }; - - func leafElementsFrom({ data } : Leaf, compare : (T, T) -> Order.Order, element : T) : Types.Iter { - var i = switch (BinarySearch.binarySearchNode(data.elements, compare, element, data.count)) { - case (#elementFound(i)) i; - case (#notFound(i)) i - }; - object { - public func next() : ?T { - if (i >= data.count) { - null - } else { - let res = data.elements[i]; - i += 1; - res - } - } - } - }; - - func reverseLeafElements({ data } : Leaf) : Types.Iter { - var i : Nat = data.count; - object { - public func next() : ?T { - if (i == 0) { - null - } else { - let res = data.elements[i - 1]; - i -= 1; - res - } - } - } - }; - - func reverseLeafElementsFrom({ data } : Leaf, compare : (T, T) -> Order.Order, element : T) : Types.Iter { - var i = switch (BinarySearch.binarySearchNode(data.elements, compare, element, data.count)) { - case (#elementFound(i)) i + 1; // +1 to include this element - case (#notFound(i)) i // i is the index of the first element greater than the search element, or count if all elements are less than the search element - }; - object { - public func next() : ?T { - if (i == 0) { - null - } else { - let res = data.elements[i - 1]; - i -= 1; - res - } - } - } - }; - - // Cursor type that keeps track of the current node and the current element index in the node - type NodeCursor = { node : Node; elementIndex : Nat }; - - func internalElements(internal : Internal) : Types.Iter { - // The nodeCursorStack keeps track of the current node and the current element index in the node - // We use a stack here to push to/pop off the next node cursor to visit - let nodeCursorStack = initializeForwardNodeCursorStack(internal); - internalElementsFromStack(nodeCursorStack) - }; - - func internalElementsFrom(internal : Internal, compare : (T, T) -> Order.Order, element : T) : Types.Iter { - let nodeCursorStack = initializeForwardNodeCursorStackFrom(internal, compare, element); - internalElementsFromStack(nodeCursorStack) - }; - - func internalElementsFromStack(nodeCursorStack : Stack.Stack>) : Types.Iter { - object { - public func next() : ?T { - // pop the next node cursor off the stack - var nodeCursor = Stack.pop(nodeCursorStack); - switch (nodeCursor) { - case null { return null }; - case (?{ node; elementIndex }) { - switch (node) { - // if a leaf node, iterate through the leaf node's next element - case (#leaf(leafNode)) { - let lastIndex = leafNode.data.count - 1 : Nat; - if (elementIndex > lastIndex) { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalElements(), leaf elementIndex out of bounds") - }; - - let currentElement = switch (leafNode.data.elements[elementIndex]) { - case (?element) { element }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Set.internalElements(), null element found in leaf node." - # "leafNode.data.count=" # debug_show (leafNode.data.count) # ", elementIndex=" # debug_show (elementIndex) - ) - } - }; - // if not at the last element, push the next element index of the leaf onto the stack and return the current element - if (elementIndex < lastIndex) { - Stack.push( - nodeCursorStack, - { - node = #leaf(leafNode); - elementIndex = elementIndex + 1 : Nat - } - ) - }; - - ?currentElement - }; - // if an internal node - case (#internal(internalNode)) { - let lastIndex = internalNode.data.count - 1 : Nat; - // Developer facing message in case of a bug - if (elementIndex > lastIndex) { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalElements(), internal elementIndex out of bounds") - }; - - let currentElement = switch (internalNode.data.elements[elementIndex]) { - case (?element) { element }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Set.internalElements(), null element found in internal node. " # - "internal.data.count=" # debug_show (internalNode.data.count) # ", elementIndex=" # debug_show (elementIndex) - ) - } - }; - - let nextCursor = { - node = #internal(internalNode); - elementIndex = elementIndex + 1 : Nat - }; - // if not the last element, push the next element of the internal node onto the stack - if (elementIndex < lastIndex) { - Stack.push(nodeCursorStack, nextCursor) - }; - // traverse the next child's min subtree and push the resulting node cursors onto the stack - // then return the current element of the internal node - traverseMinSubtreeIter(nodeCursorStack, nextCursor); - ?currentElement - } - } - } - } - } - } - }; - - func reverseInternalElements(internal : Internal) : Types.Iter { - // The nodeCursorStack keeps track of the current node and the current element index in the node - // We use a stack here to push to/pop off the next node cursor to visit - let nodeCursorStack = initializeReverseNodeCursorStack(internal); - reverseInternalElementsFromStack(nodeCursorStack) - }; - - func reverseInternalElementsFrom(internal : Internal, compare : (T, T) -> Order.Order, element : T) : Types.Iter { - let nodeCursorStack = initializeReverseNodeCursorStackFrom(internal, compare, element); - reverseInternalElementsFromStack(nodeCursorStack) - }; - - func reverseInternalElementsFromStack(nodeCursorStack : Stack.Stack>) : Types.Iter { - object { - public func next() : ?T { - // pop the next node cursor off the stack - var nodeCursor = Stack.pop(nodeCursorStack); - switch (nodeCursor) { - case null { return null }; - case (?{ node; elementIndex }) { - let firstIndex = 0 : Nat; - assert (elementIndex > firstIndex); - switch (node) { - // if a leaf node, reverse iterate through the leaf node's next element - case (#leaf(leafNode)) { - let currentElement = switch (leafNode.data.elements[elementIndex - 1]) { - case (?element) { element }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Set.reverseInternalElements(), null element found in leaf node." - # "leafNode.data.count=" # debug_show (leafNode.data.count) # ", elementIndex=" # debug_show (elementIndex) - ) - } - }; - // if not at the last element, push the previous element index of the leaf onto the stack and return the current element - if (elementIndex - 1 : Nat > firstIndex) { - Stack.push( - nodeCursorStack, - { - node = #leaf(leafNode); - elementIndex = elementIndex - 1 : Nat - } - ) - }; - - // return the current element - ?currentElement - }; - // if an internal node - case (#internal(internalNode)) { - let currentElement = switch (internalNode.data.elements[elementIndex - 1]) { - case (?element) { element }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Set.reverseInternalElements(), null element found in internal node. " # - "internal.data.count=" # debug_show (internalNode.data.count) # ", elementIndex=" # debug_show (elementIndex) - ) - } - }; - - let previousCursor = { - node = #internal(internalNode); - elementIndex = elementIndex - 1 : Nat - }; - // if not the first element, push the previous element index of the internal node onto the stack - if (elementIndex - 1 : Nat > firstIndex) { - Stack.push(nodeCursorStack, previousCursor) - }; - // traverse the previous child's max subtree and push the resulting node cursors onto the stack - // then return the current element of the internal node - traverseMaxSubtreeIter(nodeCursorStack, previousCursor); - ?currentElement - } - } - } - } - } - } - }; - - func initializeForwardNodeCursorStack(internal : Internal) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - elementIndex = 0 - }; - - // push the initial cursor to the stack - Stack.push(nodeCursorStack, nodeCursor); - // then traverse left - traverseMinSubtreeIter(nodeCursorStack, nodeCursor); - nodeCursorStack - }; - - func initializeForwardNodeCursorStackFrom(internal : Internal, compare : (T, T) -> Order.Order, element : T) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - elementIndex = 0 - }; - - traverseMinSubtreeIterFrom(nodeCursorStack, nodeCursor, compare, element); - nodeCursorStack - }; - - func initializeReverseNodeCursorStack(internal : Internal) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - elementIndex = internal.data.count - }; - - // push the initial cursor to the stack - Stack.push(nodeCursorStack, nodeCursor); - // then traverse left - traverseMaxSubtreeIter(nodeCursorStack, nodeCursor); - nodeCursorStack - }; - - func initializeReverseNodeCursorStackFrom(internal : Internal, compare : (T, T) -> Order.Order, element : T) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - elementIndex = internal.data.count - }; - - traverseMaxSubtreeIterFrom(nodeCursorStack, nodeCursor, compare, element); - nodeCursorStack - }; - - // traverse the min subtree of the current node cursor, passing each new element to the node cursor stack - func traverseMinSubtreeIter(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor) { - var currentNode = nodeCursor.node; - var childIndex = nodeCursor.elementIndex; - - label l loop { - switch (currentNode) { - // If currentNode is leaf, have hit the minimum element of the subtree and already pushed it's cursor to the stack - // so can return - case (#leaf(_)) { - return - }; - // If currentNode is internal, add it's left most child to the stack and continue traversing - case (#internal(internalNode)) { - switch (internalNode.children[childIndex]) { - // Push the next min (left most) child node to the stack - case (?childNode) { - childIndex := 0; - currentNode := childNode; - Stack.push( - nodeCursorStack, - { - node = currentNode; - elementIndex = childIndex - } - ) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.traverseMinSubtreeIter(), null child node error") - } - } - } - } - } - }; - - func traverseMinSubtreeIterFrom(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor, compare : (T, T) -> Order.Order, element : T) { - var currentNode = nodeCursor.node; - - label l loop { - let (node, childrenOption) = switch (currentNode) { - case (#leaf(leafNode)) (leafNode, null); - case (#internal(internalNode)) (internalNode, ?internalNode.children) - }; - let (i, isFound) = switch (NodeUtil.getElementIndex(node.data, compare, element)) { - case (#elementFound(i)) (i, true); - case (#notFound(i)) (i, false) - }; - if (i < node.data.count) { - Stack.push( - nodeCursorStack, - { - node = currentNode; - elementIndex = i // greater elements to traverse - } - ) - }; - if isFound return; - let ?children = childrenOption else return; - let ?childNode = children[i] else Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.traverseMinSubtreeIterFrom(), null child node error"); - currentNode := childNode - } - }; - - // traverse the max subtree of the current node cursor, passing each new element to the node cursor stack - func traverseMaxSubtreeIter(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor) { - var currentNode = nodeCursor.node; - var childIndex = nodeCursor.elementIndex; - - label l loop { - switch (currentNode) { - // If currentNode is leaf, have hit the maximum element of the subtree and already pushed it's cursor to the stack - // so can return - case (#leaf(_)) { - return - }; - // If currentNode is internal, add it's right most child to the stack and continue traversing - case (#internal(internalNode)) { - assert (childIndex <= internalNode.data.count); // children are one more than data elements - switch (internalNode.children[childIndex]) { - // Push the next max (right most) child node to the stack - case (?childNode) { - childIndex := switch (childNode) { - case (#internal(internalNode)) internalNode.data.count; - case (#leaf(leafNode)) leafNode.data.count - }; - currentNode := childNode; - Stack.push( - nodeCursorStack, - { - node = currentNode; - elementIndex = childIndex - } - ) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.traverseMaxSubtreeIter(), null child node error") - } - } - } - } - } - }; - - func traverseMaxSubtreeIterFrom(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor, compare : (T, T) -> Order.Order, element : T) { - var currentNode = nodeCursor.node; - - label l loop { - let (node, childrenOption) = switch (currentNode) { - case (#leaf(leafNode)) (leafNode, null); - case (#internal(internalNode)) (internalNode, ?internalNode.children) - }; - let (i, isFound) = switch (NodeUtil.getElementIndex(node.data, compare, element)) { - case (#elementFound(i)) (i + 1, true); // +1 to include this element - case (#notFound(i)) (i, false) // i is the index of the first element less than the search element, or 0 if all elements are greater than the search element - }; - if (i > 0) { - Stack.push( - nodeCursorStack, - { - node = currentNode; - elementIndex = i - } - ) - }; - if isFound return; - let ?children = childrenOption else return; - let ?childNode = children[i] else Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.traverseMaxSubtreeIterFrom(), null child node error"); - currentNode := childNode - } - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateInternalDeleteResult = { - // element was deleted - #deleted; - // element was absent - #inexistent; - // deleted an element, but was unable to successfully borrow and rebalance at the previous level without merging children - // the internalChild is the merged child that needs to be rebalanced at the next level up in the BTree - #mergeChild : { - internalChild : Internal - } - }; - - func internalDeleteHelper(internalNode : Internal, order : Nat, compare : (T, T) -> Order.Order, deleteElement : T, skipNode : Bool) : IntermediateInternalDeleteResult { - let minElements = NodeUtil.minElementsFromOrder(order); - let elementIndex = NodeUtil.getElementIndex(internalNode.data, compare, deleteElement); - - // match on both the result of the node binary search, and if this node level should be skipped even if the element is found (internal element replacement case) - switch (elementIndex, skipNode) { - // if element is found in the internal node - case (#elementFound(deleteIndex), false) { - if (Option.isNull(internalNode.data.elements[deleteIndex])) { - Runtime.trap("Bug in Set.internalDeleteHelper") - }; - // TODO: (optimization) replace with deletion in one step without having to retrieve the max element first - let replaceElement = NodeUtil.getMaxElement(internalNode.children[deleteIndex]); - internalNode.data.elements[deleteIndex] := ?replaceElement; - switch (internalDeleteHelper(internalNode, order, compare, replaceElement, true)) { - case (#deleted) { #deleted }; - case (#inexistent) { #inexistent }; - case (#mergeChild({ internalChild })) { - #mergeChild({ internalChild }) - } - } - }; - // if element is not found in the internal node OR the element is found, but skipping this node (because deleting the in order precessor i.e. replacement element) - // in both cases need to descend and traverse to find the element to delete - case ((#elementFound(_), true) or (#notFound(_), _)) { - let childIndex = switch (elementIndex) { - case (#elementFound(replacedSkipElementIndex)) { - replacedSkipElementIndex - }; - case (#notFound(childIndex)) { childIndex } - }; - let child = switch (internalNode.children[childIndex]) { - case (?c) { c }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalDeleteHelper, child index of #elementFound or #notfound is null") - } - }; - switch (child) { - // if child is internal - case (#internal(internalChild)) { - switch (internalDeleteHelper(internalChild, order, compare, deleteElement, false), childIndex == 0) { - // if element was successfully deleted and no additional tree re-balancing is needed, return #deleted - case (#deleted, _) { #deleted }; - case (#inexistent, _) { #inexistent }; - // if internalChild needs rebalancing and pulling child is left most - case (#mergeChild({ internalChild }), true) { - // try to pull left-most element and child from right sibling - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex + 1, #successor)) { - // if can pull up sibling element and child - case (#borrowed({ deletedSiblingElement; child })) { - NodeUtil.rotateBorrowedElementsAndChildFromSibling( - internalNode, - childIndex, - deletedSiblingElement, - child, - internalChild, - #right - ); - #deleted - }; - // unable to pull from sibling, need to merge with right sibling and push down parent - case (#notEnoughElements(sibling)) { - // get the parent element that will be pushed down the the child - let elementsToBePushedToChild = ?BTreeHelper.deleteAndShift(internalNode.data.elements, 0); - internalNode.data.count -= 1; - // merge the children and push down the parent - let newChild = NodeUtil.mergeChildrenAndPushDownParent(internalChild, elementsToBePushedToChild, sibling); - // update children of the parent - internalNode.children[0] := ?#internal(newChild); - ignore ?BTreeHelper.deleteAndShift(internalNode.children, 1); - - if (internalNode.data.count < minElements) { - #mergeChild({ internalChild = internalNode }) - } else { - #deleted - } - } - } - }; - // if internalChild needs rebalancing and pulling child is > 0, so a left sibling exists - case (#mergeChild({ internalChild }), false) { - // try to pull right-most element and its child directly from left sibling - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex - 1 : Nat, #predecessor)) { - case (#borrowed({ deletedSiblingElement; child })) { - NodeUtil.rotateBorrowedElementsAndChildFromSibling( - internalNode, - childIndex - 1 : Nat, - deletedSiblingElement, - child, - internalChild, - #left - ); - #deleted - }; - // unable to pull from left sibling - case (#notEnoughElements(leftSibling)) { - // if child is not last index, try to pull from the right child - if (childIndex < internalNode.data.count) { - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex, #successor)) { - // if can pull up sibling element and child - case (#borrowed({ deletedSiblingElement; child })) { - NodeUtil.rotateBorrowedElementsAndChildFromSibling( - internalNode, - childIndex, - deletedSiblingElement, - child, - internalChild, - #right - ); - return #deleted - }; - // if cannot borrow, from left or right, merge (see below) - case _ {} - } - }; - - // get the parent element that will be pushed down the the child - let elementToBePushedToChild = ?BTreeHelper.deleteAndShift(internalNode.data.elements, childIndex - 1 : Nat); - internalNode.data.count -= 1; - // merge it the children and push down the parent - let newChild = NodeUtil.mergeChildrenAndPushDownParent(leftSibling, elementToBePushedToChild, internalChild); - - // update children of the parent - internalNode.children[childIndex - 1] := ?#internal(newChild); - ignore ?BTreeHelper.deleteAndShift(internalNode.children, childIndex); - - if (internalNode.data.count < minElements) { - #mergeChild({ internalChild = internalNode }) - } else { - #deleted - } - } - } - } - } - }; - // if child is leaf - case (#leaf(leafChild)) { - switch (leafDeleteHelper(leafChild, order, compare, deleteElement), childIndex == 0) { - case (#deleted, _) { #deleted }; - case (#inexistent, _) { #inexistent }; - // if delete child is left most, try to borrow from right child - case (#mergeLeafData({ leafDeleteIndex }), true) { - switch (NodeUtil.borrowFromRightLeafChild(internalNode.children, childIndex)) { - case (?borrowedElement) { - let elementToBePushedToChild = internalNode.data.elements[childIndex]; - internalNode.data.elements[childIndex] := ?borrowedElement; - - ignore BTreeHelper.insertAtPostionAndDeleteAtPosition(leafChild.data.elements, elementToBePushedToChild, leafChild.data.count - 1, leafDeleteIndex); - #deleted - }; - - case null { - // can't borrow from right child, delete from leaf and merge with right child and parent element, then push down into new leaf - let rightChild = switch (internalNode.children[childIndex + 1]) { - case (?#leaf(rc)) { rc }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalDeleteHelper, if trying to borrow from right leaf child is null, rightChild index cannot be null or internal") - } - }; - let mergedLeaf = mergeParentWithLeftRightChildLeafNodesAndDelete( - internalNode.data.elements[childIndex], - leafChild, - rightChild, - leafDeleteIndex, - #left - ); - // delete the left most internal node element, since was merging from a deletion in left most child (0) and the parent element was pushed into the mergedLeaf - ignore BTreeHelper.deleteAndShift(internalNode.data.elements, 0); - // update internal node children - BTreeHelper.replaceTwoWithElementAndShift>(internalNode.children, #leaf(mergedLeaf), 0); - internalNode.data.count -= 1; - - if (internalNode.data.count < minElements) { - #mergeChild({ - internalChild = internalNode - }) - } else { - #deleted - } - - } - } - }; - // if delete child is middle or right most, try to borrow from left child - case (#mergeLeafData({ leafDeleteIndex }), false) { - // if delete child is right most, try to borrow from left child - switch (NodeUtil.borrowFromLeftLeafChild(internalNode.children, childIndex)) { - case (?borrowedElement) { - let elementToBePushedToChild = internalNode.data.elements[childIndex - 1]; - internalNode.data.elements[childIndex - 1] := ?borrowedElement; - ignore BTreeHelper.insertAtPostionAndDeleteAtPosition(leafChild.data.elements, elementToBePushedToChild, 0, leafDeleteIndex); - #deleted - }; - case null { - // if delete child is in the middle, try to borrow from right child - if (childIndex < internalNode.data.count) { - // try to borrow from right - switch (NodeUtil.borrowFromRightLeafChild(internalNode.children, childIndex)) { - case (?borrowedElement) { - let elementToBePushedToChild = internalNode.data.elements[childIndex]; - internalNode.data.elements[childIndex] := ?borrowedElement; - // insert the successor at the very last element - ignore BTreeHelper.insertAtPostionAndDeleteAtPosition(leafChild.data.elements, elementToBePushedToChild, leafChild.data.count - 1, leafDeleteIndex); - return #deleted - }; - // if cannot borrow, from left or right, merge (see below) - case _ {} - } - }; - - // can't borrow from left child, delete from leaf and merge with left child and parent element, then push down into new leaf - let leftChild = switch (internalNode.children[childIndex - 1]) { - case (?#leaf(lc)) { lc }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalDeleteHelper, if trying to borrow from left leaf child is null, then left child index must not be null or internal") - } - }; - let mergedLeaf = mergeParentWithLeftRightChildLeafNodesAndDelete( - internalNode.data.elements[childIndex - 1], - leftChild, - leafChild, - leafDeleteIndex, - #right - ); - // delete the right most internal node element, since was merging from a deletion in the right most child and the parent element was pushed into the mergedLeaf - ignore BTreeHelper.deleteAndShift(internalNode.data.elements, childIndex - 1); - // update internal node children - BTreeHelper.replaceTwoWithElementAndShift>(internalNode.children, #leaf(mergedLeaf), childIndex - 1); - internalNode.data.count -= 1; - - if (internalNode.data.count < minElements) { - #mergeChild({ - internalChild = internalNode - }) - } else { - #deleted - } - } - } - } - } - } - } - } - } - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateLeafDeleteResult = { - // element was deleted - #deleted; - // element was absent - #inexistent; - // leaf had the minimum number of elements when deleting, so returns the leaf node's data and the index of the element that will be deleted - #mergeLeafData : { - data : Data; - leafDeleteIndex : Nat - } - }; - - func leafDeleteHelper(leafNode : Leaf, order : Nat, compare : (T, T) -> Order.Order, deleteElement : T) : IntermediateLeafDeleteResult { - let minElements = NodeUtil.minElementsFromOrder(order); - - switch (NodeUtil.getElementIndex(leafNode.data, compare, deleteElement)) { - case (#elementFound(deleteIndex)) { - if (leafNode.data.count > minElements) { - leafNode.data.count -= 1; - ignore BTreeHelper.deleteAndShift(leafNode.data.elements, deleteIndex); - #deleted - } else { - #mergeLeafData({ - data = leafNode.data; - leafDeleteIndex = deleteIndex - }) - } - }; - case (#notFound(_)) { - #inexistent - } - } - }; - - func containsInInternal(internalNode : Internal, compare : (T, T) -> Order.Order, element : T) : Bool { - switch (NodeUtil.getElementIndex(internalNode.data, compare, element)) { - case (#elementFound _index) { - true - }; - case (#notFound(index)) { - switch (internalNode.children[index]) { - // expects the child to be there, otherwise there's a bug in binary search or the tree is invalid - case null { Runtime.trap("Internal bug: Set.containsInInternal") }; - case (?#leaf(leafNode)) { containsInLeaf(leafNode, compare, element) }; - case (?#internal(internalNode)) { - containsInInternal(internalNode, compare, element) - } - } - } - } - }; - - func containsInLeaf(leafNode : Leaf, compare : (T, T) -> Order.Order, element : T) : Bool { - switch (NodeUtil.getElementIndex(leafNode.data, compare, element)) { - case (#elementFound(_index)) { - true - }; - case _ false - } - }; - - type DeletionSide = { #left; #right }; - - func mergeParentWithLeftRightChildLeafNodesAndDelete( - parentElement : ?T, - leftChild : Leaf, - rightChild : Leaf, - deleteIndex : Nat, - deletionSide : DeletionSide - ) : Leaf { - let count = leftChild.data.count * 2; - let (elements, _) = BTreeHelper.mergeParentWithChildrenAndDelete( - parentElement, - leftChild.data.count, - leftChild.data.elements, - rightChild.data.elements, - deleteIndex, - deletionSide - ); - ({ - data = { - elements; - var count = count - } - }) - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateInsertResult = { - // element was inserted - #inserted; - // element was alreay present - #existent; - // child was full when inserting, so returns the promoted element and the split left and right child - #promote : { - element : T; - leftChild : Node; - rightChild : Node - } - }; - - // Helper for inserting into a leaf node - func leafInsertHelper(leafNode : Leaf, order : Nat, compare : (T, T) -> Order.Order, insertedElement : T) : (IntermediateInsertResult) { - // Perform binary search to see if the element exists in the node - switch (NodeUtil.getElementIndex(leafNode.data, compare, insertedElement)) { - case (#elementFound(insertIndex)) { - let previous = leafNode.data.elements[insertIndex]; - leafNode.data.elements[insertIndex] := ?insertedElement; - switch (previous) { - case (?_) { #existent }; - case null { Runtime.trap("Bug in Set.leafInsertHelper") }; // the binary search already found an element, so this case should never happen - } - }; - case (#notFound(insertIndex)) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - let maxElements : Nat = order - 1; - // If the leaf is full, insert, split the node, and promote the middle element - if (leafNode.data.count >= maxElements) { - let (leftElements, promotedParentElement, rightElements) = BTreeHelper.insertOneAtIndexAndSplitArray( - leafNode.data.elements, - insertedElement, - insertIndex - ); - - let leftCount = order / 2; - let rightCount : Nat = if (order % 2 == 0) { leftCount - 1 } else { - leftCount - }; - - ( - #promote({ - element = promotedParentElement; - leftChild = createLeaf(leftElements, leftCount); - rightChild = createLeaf(rightElements, rightCount) - }) - ) - } - // Otherwise, insert at the specified index (shifting elements over if necessary) - else { - NodeUtil.insertAtIndexOfNonFullNodeData(leafNode.data, ?insertedElement, insertIndex); - #inserted - } - } - } - }; - - // Helper for inserting into an internal node - func internalInsertHelper(internalNode : Internal, order : Nat, compare : (T, T) -> Order.Order, insertElement : T) : IntermediateInsertResult { - switch (NodeUtil.getElementIndex(internalNode.data, compare, insertElement)) { - case (#elementFound(insertIndex)) { - let previous = internalNode.data.elements[insertIndex]; - internalNode.data.elements[insertIndex] := ?insertElement; - switch (previous) { - case (?_) { #existent }; - case null { - Runtime.trap("Bug in Set.internalInsertHelper, element found") - }; // the binary search already found an element, so this case should never happen - } - }; - case (#notFound(insertIndex)) { - let insertResult = switch (internalNode.children[insertIndex]) { - case null { - Runtime.trap("Bug in Set.internalInsertHelper, not found") - }; - case (?#leaf(leafNode)) { - leafInsertHelper(leafNode, order, compare, insertElement) - }; - case (?#internal(internalChildNode)) { - internalInsertHelper(internalChildNode, order, compare, insertElement) - } - }; - - switch (insertResult) { - case (#inserted) #inserted; - case (#existent) #existent; - case (#promote({ element = promotedElement; leftChild; rightChild })) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - let maxElements : Nat = order - 1; - // if current internal node is full, need to split the internal node - if (internalNode.data.count >= maxElements) { - // insert and split internal elements, determine new promotion target element - let (leftElements, promotedParentElement, rightElements) = BTreeHelper.insertOneAtIndexAndSplitArray( - internalNode.data.elements, - promotedElement, - insertIndex - ); - - // calculate the element count in the left elements and the element count in the right elements - let leftCount = order / 2; - let rightCount : Nat = if (order % 2 == 0) { leftCount - 1 } else { - leftCount - }; - - // split internal children - let (leftChildren, rightChildren) = NodeUtil.splitChildrenInTwoWithRebalances( - internalNode.children, - insertIndex, - leftChild, - rightChild - ); - - // send the element to be promoted, as well as the internal children left and right split - #promote({ - element = promotedParentElement; - leftChild = #internal({ - data = { elements = leftElements; var count = leftCount }; - children = leftChildren - }); - rightChild = #internal({ - data = { elements = rightElements; var count = rightCount }; - children = rightChildren - }) - }) - } else { - // insert the new elements into the internal node - NodeUtil.insertAtIndexOfNonFullNodeData(internalNode.data, ?promotedElement, insertIndex); - // split and re-insert the single child that needs rebalancing - NodeUtil.insertRebalancedChild(internalNode.children, insertIndex, leftChild, rightChild); - #inserted - } - } - } - } - } - }; - - func createLeaf(elements : [var ?T], count : Nat) : Node { - #leaf({ - data = { - elements; - var count - } - }) - }; - - // FIXME - // Additional functionality compared to original source. - - func cloneData(data : Data) : Data { - { - elements = VarArray.clone(data.elements); - var count = data.count - } - }; - - func cloneNode(node : Node) : Node { - switch node { - case (#leaf { data }) { - #leaf { data = cloneData(data) } - }; - case (#internal { data; children }) { - let clonedData = cloneData(data); - let clonedChildren = VarArray.map, ?Node>( - children, - func child { - switch child { - case null null; - case (?childNode) ?cloneNode(childNode) - } - } - ); - #internal({ - data = clonedData; - children = clonedChildren - }) - } - } - }; - - module BinarySearch { - public type SearchResult = { - #elementFound : Nat; - #notFound : Nat - }; - - /// Searches an array for a specific element, returning the index it occurs at if #elementFound, or the child/insert index it may occur at - /// if #notFound. This is used when determining if a element exists in an internal or leaf node, where an element should be inserted in a - /// leaf node, or which child of an internal node a element could be in. - /// - /// Note: This function expects a mutable, nullable, array of elements in sorted order, where all nulls appear at the end of the array. - /// This function may trap if a null element appears before any elements. It also expects a maxIndex, which is the right-most index (bound) - /// from which to begin the binary search (the left most bound is expected to be 0) - /// - /// Parameters: - /// - /// * array - the sorted array that the binary search is performed upon - /// * compare - the comparator used to perform the search - /// * searchElement - the element being compared against in the search - /// * maxIndex - the right-most index (bound) from which to begin the search - public func binarySearchNode(array : [var ?T], compare : (T, T) -> Order.Order, searchElement : T, maxIndex : Nat) : SearchResult { - // TODO: get rid of this check? - // Trap if array is size 0 (should not happen) - if (array.size() == 0) { - assert false - }; - - // if all elements in the array are null (i.e. first element is null), return #notFound(0) - if (maxIndex == 0) { - return #notFound(0) - }; - - // Initialize search from first to last index - var left : Nat = 0; - var right = maxIndex; // maxIndex does not necessarily mean array.size() - 1 - // Search the array - while (left < right) { - let middle = (left + right) / 2; - switch (array[middle]) { - case null { assert false }; - case (?element) { - switch (compare(searchElement, element)) { - // If the element is present at the middle itself - case (#equal) { return #elementFound(middle) }; - // If element is greater than mid, it can only be present in left subarray - case (#greater) { left := middle + 1 }; - // If element is smaller than mid, it can only be present in right subarray - case (#less) { - right := if (middle == 0) { 0 } else { middle - 1 } - } - } - } - } - }; - - if (left == array.size()) { - return #notFound(left) - }; - - // left == right - switch (array[left]) { - // inserting at end of array - case null { #notFound(left) }; - case (?element) { - switch (compare(searchElement, element)) { - // if left is the searched element - case (#equal) { #elementFound(left) }; - // if the element is not found, return notFound and the insert location - case (#greater) { #notFound(left + 1) }; - case (#less) { #notFound(left) } - } - } - } - } - }; - - module NodeUtil { - /// Inserts element at the given index into a non-full leaf node - public func insertAtIndexOfNonFullNodeData(data : Data, element : ?T, insertIndex : Nat) { - let currentLastElementIndex : Nat = if (data.count == 0) { 0 } else { - data.count - 1 - }; - BTreeHelper.insertAtPosition(data.elements, element, insertIndex, currentLastElementIndex); - - // increment the count of data in this node since just inserted an element - data.count += 1 - }; - - /// Inserts two rebalanced (split) child halves into a non-full array of children. - public func insertRebalancedChild(children : [var ?Node], rebalancedChildIndex : Nat, leftChildInsert : Node, rightChildInsert : Node) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - var j : Nat = children.size() - 2; - - // This is just a sanity check to ensure the children aren't already full (should split promote otherwise) - // TODO: Remove this check once confident - if (Option.isSome(children[j + 1])) { assert false }; - - // Iterate backwards over the array and shift each element over to the right by one until the rebalancedChildIndex is hit - while (j > rebalancedChildIndex) { - children[j + 1] := children[j]; - j -= 1 - }; - - // Insert both the left and right rebalanced children (replacing the pre-split child) - children[j] := ?leftChildInsert; - children[j + 1] := ?rightChildInsert - }; - - /// Used when splitting the children of an internal node - /// - /// Takes in the rebalanced child index, as well as both halves of the rebalanced child and splits the children, inserting the left and right child halves appropriately - /// - /// For more context, see the documentation for the splitArrayAndInsertTwo method in ArrayUtils.mo - public func splitChildrenInTwoWithRebalances( - children : [var ?Node], - rebalancedChildIndex : Nat, - leftChildInsert : Node, - rightChildInsert : Node - ) : ([var ?Node], [var ?Node]) { - BTreeHelper.splitArrayAndInsertTwo>(children, rebalancedChildIndex, leftChildInsert, rightChildInsert) - }; - - /// Helper used to get the element index of of a element within a node - /// - /// for more, see the BinarySearch.binarySearchNode() documentation - public func getElementIndex(data : Data, compare : (T, T) -> Order.Order, element : T) : BinarySearch.SearchResult { - BinarySearch.binarySearchNode(data.elements, compare, element, data.count) - }; - - // calculates a BTree Node's minimum allowed elements given the order of the BTree - public func minElementsFromOrder(order : Nat) : Nat { - if (order % 2 == 0) { order / 2 - 1 } else { order / 2 } - }; - - // Given a node, get the maximum element (right most leaf element) - public func getMaxElement(node : ?Node) : T { - switch (node) { - case (?#leaf({ data })) { - switch (data.elements[data.count - 1]) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.NodeUtil.getMaxElement, data cannot have more elements than it's count") - }; - case (?element) { element } - } - }; - case (?#internal({ data; children })) { - getMaxElement(children[data.count]) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.NodeUtil.getMaxElement, the node provided cannot be null") - } - } - }; - - type InorderBorrowType = { - #predecessor; - #successor - }; - - // attempts to retrieve the in max element of the child leaf node directly to the left if the node will allow it - // returns the deleted max element if able to retrieve, null if not able - // - // mutates the predecessing node's elements - public func borrowFromLeftLeafChild(children : [var ?Node], ofChildIndex : Nat) : ?T { - let predecessorIndex : Nat = ofChildIndex - 1; - borrowFromLeafChild(children, predecessorIndex, #predecessor) - }; - - // attempts to retrieve the in max element of the child leaf node directly to the right if the node will allow it - // returns the deleted max element if able to retrieve, null if not able - // - // mutates the predecessing node's elements - public func borrowFromRightLeafChild(children : [var ?Node], ofChildIndex : Nat) : ?T { - borrowFromLeafChild(children, ofChildIndex + 1, #successor) - }; - - func borrowFromLeafChild(children : [var ?Node], borrowChildIndex : Nat, childSide : InorderBorrowType) : ?T { - let minElements = minElementsFromOrder(children.size()); - - switch (children[borrowChildIndex]) { - case (?#leaf({ data })) { - if (data.count > minElements) { - // able to borrow an element from this child, so decrement the count of elements - data.count -= 1; // Since enforce order >= 4, there will always be at least 1 element per node - switch (childSide) { - case (#predecessor) { - let deletedElement = data.elements[data.count]; - data.elements[data.count] := null; - deletedElement - }; - case (#successor) { - ?BTreeHelper.deleteAndShift(data.elements, 0) - } - } - } else { null } - }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.NodeUtil.borrowFromLeafChild, the node at the borrow child index cannot be null or internal") - } - } - }; - - type InternalBorrowResult = { - #borrowed : InternalBorrow; - #notEnoughElements : Internal - }; - - type InternalBorrow = { - deletedSiblingElement : ?T; - child : ?Node - }; - - // Attempts to borrow an element and child from an internal sibling node - public func borrowFromInternalSibling(children : [var ?Node], borrowChildIndex : Nat, borrowType : InorderBorrowType) : InternalBorrowResult { - let minElements = minElementsFromOrder(children.size()); - - switch (children[borrowChildIndex]) { - case (?#internal({ data; children })) { - if (data.count > minElements) { - data.count -= 1; - switch (borrowType) { - case (#predecessor) { - let deletedSiblingElement = data.elements[data.count]; - data.elements[data.count] := null; - let child = children[data.count + 1]; - children[data.count + 1] := null; - #borrowed({ - deletedSiblingElement; - child - }) - }; - case (#successor) { - #borrowed({ - deletedSiblingElement = ?BTreeHelper.deleteAndShift(data.elements, 0); - child = ?BTreeHelper.deleteAndShift(children, 0) - }) - } - } - } else { #notEnoughElements({ data; children }) } - }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.NodeUtil.borrowFromInternalSibling from internal sibling, the child at the borrow index cannot be null or a leaf") - } - } - }; - - type SiblingSide = { #left; #right }; - - // Rotates the borrowed elements and child from sibling side of the internal node to the internal child recipient - public func rotateBorrowedElementsAndChildFromSibling( - internalNode : Internal, - parentRotateIndex : Nat, - borrowedSiblingElement : ?T, - borrowedSiblingChild : ?Node, - internalChildRecipient : Internal, - siblingSide : SiblingSide - ) { - // if borrowing from the left, the rotated element and child will always be inserted first - // if borrowing from the right, the rotated element and child will always be inserted last - let (elementIndex, childIndex) = switch (siblingSide) { - case (#left) { (0, 0) }; - case (#right) { - (internalChildRecipient.data.count, internalChildRecipient.data.count + 1) - } - }; - - // get the parent element that will be pushed down the the child - let elementToBePushedToChild = internalNode.data.elements[parentRotateIndex]; - // replace the parent with the sibling element - internalNode.data.elements[parentRotateIndex] := borrowedSiblingElement; - // push the element and child down into the internalChild - insertAtIndexOfNonFullNodeData(internalChildRecipient.data, elementToBePushedToChild, elementIndex); - - BTreeHelper.insertAtPosition>(internalChildRecipient.children, borrowedSiblingChild, childIndex, internalChildRecipient.data.count) - }; - - // Merges the elements and children of two internal nodes, pushing the parent element in between the right and left halves - public func mergeChildrenAndPushDownParent(leftChild : Internal, parentElement : ?T, rightChild : Internal) : Internal { - { - data = mergeData(leftChild.data, parentElement, rightChild.data); - children = mergeChildren(leftChild.children, rightChild.children) - } - }; - - func mergeData(leftData : Data, parentElement : ?T, rightData : Data) : Data { - assert leftData.count <= minElementsFromOrder(leftData.elements.size() + 1); - assert rightData.count <= minElementsFromOrder(rightData.elements.size() + 1); - - let mergedElements = VarArray.repeat(null, leftData.elements.size()); - var i = 0; - while (i < leftData.count) { - mergedElements[i] := leftData.elements[i]; - i += 1 - }; - - mergedElements[i] := parentElement; - i += 1; - - var j = 0; - while (j < rightData.count) { - mergedElements[i] := rightData.elements[j]; - i += 1; - j += 1 - }; - - { - elements = mergedElements; - var count = leftData.count + 1 + rightData.count - } - }; - - func mergeChildren(leftChildren : [var ?Node], rightChildren : [var ?Node]) : [var ?Node] { - let mergedChildren = VarArray.repeat>(null, leftChildren.size()); - var i = 0; - - while (Option.isSome(leftChildren[i])) { - mergedChildren[i] := leftChildren[i]; - i += 1 - }; - - var j = 0; - while (Option.isSome(rightChildren[j])) { - mergedChildren[i] := rightChildren[j]; - i += 1; - j += 1 - }; - - mergedChildren - } - } -} diff --git a/.mops/core@2.4.0/src/Stack.mo b/.mops/core@2.4.0/src/Stack.mo deleted file mode 100644 index 89c099b..0000000 --- a/.mops/core@2.4.0/src/Stack.mo +++ /dev/null @@ -1,879 +0,0 @@ -/// A mutable stack data structure. -/// Elements can be pushed on top of the stack -/// and removed from top of the stack (LIFO). -/// -/// Example: -/// ```motoko -/// import Stack "mo:core/Stack"; -/// import Debug "mo:core/Debug"; -/// -/// persistent actor { -/// let levels = Stack.empty(); -/// Stack.push(levels, "Inner"); -/// Stack.push(levels, "Middle"); -/// Stack.push(levels, "Outer"); -/// assert Stack.pop(levels) == ?"Outer"; -/// assert Stack.pop(levels) == ?"Middle"; -/// assert Stack.pop(levels) == ?"Inner"; -/// assert Stack.pop(levels) == null; -/// } -/// ``` -/// -/// The internal implementation is a singly-linked list. -/// -/// Performance: -/// * Runtime: `O(1)` for push, pop, and peek operation. -/// * Space: `O(n)`. -/// `n` denotes the number of elements stored on the stack. - -// TODO: optimize or re-use pure/List operations (e.g. for `any` etc) - -import Order "Order"; -import Iter "Iter"; -import Types "Types"; -import PureList "pure/List"; - -module { - type List = Types.Pure.List; - public type Stack = Types.Stack; - - /// Convert a mutable stack to an immutable, purely functional list. - /// Please note that functional lists are ordered like stacks (FIFO). - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import PureList "mo:core/pure/List"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let mutableStack = Stack.empty(); - /// Stack.push(mutableStack, 3); - /// Stack.push(mutableStack, 2); - /// Stack.push(mutableStack, 1); - /// let immutableList = Stack.toPure(mutableStack); - /// assert Iter.toArray(PureList.values(immutableList)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the stack. - /// @deprecated M0235 - public func toPure(self : Stack) : PureList.List { - self.top - }; - - public func toArray(self : Stack) : [T] { - Iter.toArray(values(self)) - }; - - public func toVarArray(self : Stack) : [var T] { - Iter.toVarArray(values(self)) - }; - - /// Convert an immutable, purely functional list to a mutable stack. - /// Please note that functional lists are ordered like stacks (FIFO). - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import PureList "mo:core/pure/List"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let immutableList = PureList.fromIter([1, 2, 3].values()); - /// let mutableStack = Stack.fromPure(immutableList); - /// assert Iter.toArray(Stack.values(mutableStack)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the queue. - /// @deprecated M0235 - public func fromPure(list : PureList.List) : Stack { - var size = 0; - var cur = list; - loop { - switch cur { - case (?(_, next)) { - size += 1; - cur := next - }; - case null { - return { var top = list; var size } - } - } - } - }; - - public func fromVarArray(array : [var T]) : Stack { - fromIter(array.values()) - }; - - public func fromArray(array : [T]) : Stack { - fromIter(array.values()) - }; - - /// Create a new empty mutable stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// assert Stack.size(stack) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Stack { - { - var top = null; - var size = 0 - } - }; - - /// Creates a new stack with `size` elements by applying the `generator` function to indices `[0..size-1]`. - /// Elements are pushed in ascending index order. - /// Which means that the generated element with the index `0` will be at the bottom of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let stack = Stack.tabulate(3, func(i) { 2 * i }); - /// assert Iter.toArray(Stack.values(stack)) == [4, 2, 0]; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `generator` has O(1) costs. - public func tabulate(size : Nat, generator : Nat -> T) : Stack { - let stack = empty(); - var index = 0; - while (index < size) { - let element = generator(index); - push(stack, element); - index += 1 - }; - stack - }; - - /// Creates a new stack containing a single element. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.singleton("motoko"); - /// assert Stack.peek(stack) == ?"motoko"; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func singleton(element : T) : Stack { - let stack = empty(); - push(stack, element); - stack - }; - - /// Removes all elements from the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// Stack.clear(stack); - /// assert Stack.isEmpty(stack); - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func clear(self : Stack) { - self.top := null; - self.size := 0 - }; - - /// Creates a deep copy of the stack with the same elements in the same order. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let original = Stack.fromIter([3, 2, 1].values()); - /// let copy = Stack.clone(original); - /// assert Stack.equal(copy, original, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack. - public func clone(self : Stack) : Stack { - let copy = empty(); - for (element in values(self)) { - push(copy, element) - }; - reverse(copy); - copy - }; - - /// Returns true if the stack contains no elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// assert Stack.isEmpty(stack); - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func isEmpty(self : Stack) : Bool { - self.size == 0 - }; - - /// Returns the number of elements on the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.size(stack) == 3; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func size(self : Stack) : Nat { - self.size - }; - - /// Returns true if the stack contains the specified element. - /// Uses the provided equality function to compare elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.contains(stack, Nat.equal, 2); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and assuming - /// that `equal` has O(1) costs. - public func contains(self : Stack, equal : (implicit : (T, T) -> Bool), element : T) : Bool { - for (existing in values(self)) { - if (equal(existing, element)) { - return true - } - }; - false - }; - - public func reverseValues(self : Stack) : Iter.Iter { - Iter.reverse(values(self)) - }; - - /// Pushes a new element onto the top of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 42); - /// assert Stack.peek(stack) == ?42; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func push(self : Stack, value : T) { - self.top := ?(value, self.top); - self.size += 1 - }; - - /// Returns the top element of the stack without removing it. - /// Returns null if the stack is empty. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// assert Stack.peek(stack) == ?1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func peek(self : Stack) : ?T { - switch (self.top) { - case null null; - case (?(value, _)) ?value - } - }; - - /// Removes and returns the top element of the stack. - /// Returns null if the stack is empty. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// assert Stack.pop(stack) == ?1; - /// assert Stack.pop(stack) == ?2; - /// assert Stack.pop(stack) == ?3; - /// assert Stack.pop(stack) == null; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func pop(self : Stack) : ?T { - switch (self.top) { - case null null; - case (?(value, next)) { - self.top := next; - self.size -= 1; - ?value - } - } - }; - - /// Returns the element at the specified position from the top of the stack. - /// Returns null if position is out of bounds. - /// Position 0 is the top of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 'c'); - /// Stack.push(stack, 'b'); - /// Stack.push(stack, 'a'); - /// assert Stack.get(stack, 0) == ?'a'; - /// assert Stack.get(stack, 1) == ?'b'; - /// assert Stack.get(stack, 2) == ?'c'; - /// assert Stack.get(stack, 3) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack. - public func get(self : Stack, position : Nat) : ?T { - var index = 0; - var current = self.top; - while (index < position) { - switch (current) { - case null return null; - case (?(_, next)) { - current := next - } - }; - index += 1 - }; - switch (current) { - case null null; - case (?(value, _)) ?value - } - }; - - /// Reverses the order of elements in the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// Stack.reverse(stack); - /// assert Stack.pop(stack) == ?3; - /// assert Stack.pop(stack) == ?2; - /// assert Stack.pop(stack) == ?1; - /// assert Stack.pop(stack) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack. - public func reverse(self : Stack) { - var last : List = null; - for (element in values(self)) { - last := ?(element, last) - }; - self.top := last - }; - - /// Returns an iterator over the elements in the stack, from top to bottom. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// assert Iter.toArray(Stack.values(stack)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: O(1) for iterator creation, O(n) for full traversal - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack. - public func values(self : Stack) : Types.Iter { - object { - var current = self.top; - - public func next() : ?T { - switch (current) { - case null null; - case (?(value, next)) { - current := next; - ?value - } - } - } - } - }; - - /// Returns true if all elements in the stack satisfy the predicate. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([2, 4, 6].values()); - /// assert Stack.all(stack, func(n) = n % 2 == 0); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `predicate` has O(1) costs. - public func all(self : Stack, predicate : T -> Bool) : Bool { - for (element in values(self)) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Returns true if any element in the stack satisfies the predicate. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.any(stack, func(n) = n == 2); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming `predicate` has O(1) costs. - public func any(self : Stack, predicate : T -> Bool) : Bool { - for (element in values(self)) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Applies the operation to each element in the stack, from top to bottom. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// var text = ""; - /// Stack.forEach(stack, func(n) = text #= Nat.toText(n)); - /// assert text == "123"; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `operation` has O(1) costs. - public func forEach(self : Stack, operation : T -> ()) { - for (element in values(self)) { - operation(element) - } - }; - - /// Creates a new stack by applying the projection function to each element. - /// Maintains the original order of elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// let doubled = Stack.map(stack, func(n) { 2 * n }); - /// assert Stack.get(doubled, 0) == ?2; - /// assert Stack.get(doubled, 1) == ?4; - /// assert Stack.get(doubled, 2) == ?6; - /// assert Stack.get(doubled, 3) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `project` has O(1) costs. - public func map(self : Stack, project : T -> U) : Stack { - let result = empty(); - for (element in values(self)) { - push(result, project(element)) - }; - reverse(result); - result - }; - - /// Creates a new stack containing only elements that satisfy the predicate. - /// Maintains the relative order of elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 4); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// let evens = Stack.filter(stack, func(n) { n % 2 == 0 }); - /// assert Stack.pop(evens) == ?2; - /// assert Stack.pop(evens) == ?4; - /// assert Stack.pop(evens) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming `predicate` has O(1) costs. - public func filter(self : Stack, predicate : T -> Bool) : Stack { - let result = empty(); - for (element in values(self)) { - if (predicate(element)) { - push(result, element) - } - }; - reverse(result); - result - }; - - /// Creates a new stack by applying the projection function to each element - /// and keeping only the successful results (where project returns ?value). - /// Maintains the relative order of elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 4); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// let evenDoubled = Stack.filterMap(stack, func(n) { - /// if (n % 2 == 0) { - /// ?(n * 2) - /// } else { - /// null - /// } - /// }); - /// assert Stack.pop(evenDoubled) == ?4; - /// assert Stack.pop(evenDoubled) == ?8; - /// assert Stack.pop(evenDoubled) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `project` has O(1) costs. - public func filterMap(self : Stack, project : T -> ?U) : Stack { - let result = empty(); - for (element in values(self)) { - switch (project(element)) { - case null {}; - case (?newElement) { - push(result, newElement) - } - } - }; - reverse(result); - result - }; - - /// Return the first element for which the given `predicate` is true, - /// if such an element exists. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromPure(?(1, ?(2, ?(3, null)))); - /// assert Stack.find(stack, func n = n > 1) == ?2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - - public func find(self : Stack, predicate : T -> Bool) : ?T = PureList.find(self.top, predicate); - - /// Return the first index for which the given `predicate` is true. - /// If no element satisfies the predicate, returns null. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromPure(?('A', ?('B', ?('C', ?('D', null))))); - /// let found = Stack.findIndex(stack, func x = x == 'C'); - /// assert found == ?2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func findIndex(self : Stack, predicate : T -> Bool) : ?Nat = PureList.findIndex(self.top, predicate); - - /// Compares two stacks for equality using the provided equality function. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack1 = Stack.fromIter([3, 2, 1].values()); - /// let stack2 = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.equal(stack1, stack2, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `equal` has O(1) costs. - public func equal(self : Stack, other : Stack, equal : (implicit : (T, T) -> Bool)) : Bool { - if (size(self) != size(other)) { - return false - }; - let iterator1 = values(self); - let iterator2 = values(other); - loop { - let element1 = iterator1.next(); - let element2 = iterator2.next(); - switch (element1, element2) { - case (null, null) { - return true - }; - case (?element1, ?element2) { - if (not equal(element1, element2)) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Creates a new stack from an iterator. - /// Elements are pushed in iteration order. Which means that the last element - /// of the iterator will be the first element on top of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Iter.toArray(Stack.values(stack)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of iterated elements. - public func fromIter(iter : Types.Iter) : Stack { - let stack = empty(); - for (element in iter) { - push(stack, element) - }; - stack - }; - - /// Convert an iterator into a stack. - /// Elements are pushed in iteration order. Which means that the last element - /// of the iterator will be the first element on top of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = [3, 2, 1].values(); - /// - /// let stack = iter.toStack(); - /// - /// assert Iter.toArray(Stack.values(stack)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of iterated elements. - public func toStack(self : Types.Iter) : Stack { - fromIter(self) - }; - - /// Converts the stack to its string representation using the provided - /// element formatting function. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.toText(stack, Nat.toText) == "Stack[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `format` has O(1) costs. - public func toText(self : Stack, format : (implicit : (toText : T -> Text))) : Text { - var text = "Stack["; - var sep = ""; - for (element in values(self)) { - text #= sep # format(element); - sep := ", " - }; - text #= "]"; - text - }; - - /// Compares two stacks lexicographically using the provided comparison function. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack1 = Stack.fromIter([2, 1].values()); - /// let stack2 = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.compare(stack1, stack2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `compare` has O(1) costs. - public func compare(self : Stack, other : Stack, compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let iterator1 = values(self); - let iterator2 = values(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?element1, ?element2) { - let comparison = compare(element1, element2); - if (comparison != #equal) { - return comparison - } - } - } - } - } -} diff --git a/.mops/core@2.4.0/src/Text.mo b/.mops/core@2.4.0/src/Text.mo deleted file mode 100644 index 1f6c8a5..0000000 --- a/.mops/core@2.4.0/src/Text.mo +++ /dev/null @@ -1,967 +0,0 @@ -/// Utility functions for `Text` values. -/// -/// A `Text` value represents human-readable text as a sequence of characters of type `Char`. -/// -/// ```motoko -/// let text = "Hello!"; -/// let size = text.size(); -/// assert size == 6; -/// let iter = text.chars(); -/// assert iter.next() == ?'H'; -/// assert iter.next() == ?'e'; -/// assert iter.next() == ?'l'; -/// assert iter.next() == ?'l'; -/// assert iter.next() == ?'o'; -/// assert iter.next() == ?'!'; -/// assert iter.next() == null; -/// let concat = text # " 👋"; -/// assert concat == "Hello! 👋"; -/// ``` -/// -/// The `"mo:core/Text"` module defines additional operations on `Text` values. -/// -/// Import the module from the core package: -/// -/// ```motoko name=import -/// import Text "mo:core/Text"; -/// ``` -/// -/// Note: `Text` values are represented as ropes of UTF-8 character sequences with O(1) concatenation. -/// - -import Char "Char"; -import Iter "Iter"; -import Stack "Stack"; -import Types "Types"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// The type corresponding to primitive `Text` values. - /// - /// ```motoko - /// let hello = "Hello!"; - /// let emoji = "👋"; - /// let concat = hello # " " # emoji; - /// assert concat == "Hello! 👋"; - /// ``` - public type Text = Prim.Types.Text; - - /// Converts the given `Char` to a `Text` value. - /// - /// ```motoko include=import - /// let text = Text.fromChar('A'); - /// assert text == "A"; - /// ``` - public let fromChar : (c : Char) -> Text = Prim.charToText; - - /// Converts the given `[Char]` to a `Text` value. - /// - /// ```motoko include=import - /// let text = Text.fromArray(['A', 'v', 'o', 'c', 'a', 'd', 'o']); - /// assert text == "Avocado"; - /// ``` - /// - /// Runtime: O(a.size()) - /// Space: O(a.size()) - public func fromArray(a : [Char]) : Text = fromIter(a.vals()); - - /// Converts the given `[var Char]` to a `Text` value. - /// - /// ```motoko include=import - /// let text = Text.fromVarArray([var 'E', 'g', 'g', 'p', 'l', 'a', 'n', 't']); - /// assert text == "Eggplant"; - /// ``` - /// - /// Runtime: O(a.size()) - /// Space: O(a.size()) - public func fromVarArray(a : [var Char]) : Text = fromIter(a.vals()); - - /// Iterates over each `Char` value in the given `Text`. - /// - /// Equivalent to calling the `t.chars()` method where `t` is a `Text` value. - /// - /// ```motoko include=import - /// let chars = Text.toIter("abc"); - /// assert chars.next() == ?'a'; - /// assert chars.next() == ?'b'; - /// assert chars.next() == ?'c'; - /// assert chars.next() == null; - /// ``` - public func toIter(self : Text) : Iter.Iter = self.chars(); - - /// Collapses the characters in `text` into a single value by starting with `base` - /// and progessively combining characters into `base` with `combine`. Iteration runs - /// left to right. - /// - /// ```motoko include=import - /// - /// let text = "Mississippi"; - /// let count = - /// Text.foldLeft( - /// text, - /// 0, // start the sum at 0 - /// func(ss, c) = if (c == 's') ss + 1 else ss - /// ); - /// assert count == 4; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(self : Text, base : A, combine : (A, Char) -> A) : A { - var acc = base; - for (c in self.chars()) acc := combine(acc, c); - acc - }; - - /// Creates a new `Array` containing characters of the given `Text`. - /// - /// Equivalent to `Iter.toArray(t.chars())`. - /// - /// ```motoko include=import - /// assert Text.toArray("Café") == ['C', 'a', 'f', 'é']; - /// ``` - /// - /// Runtime: O(t.size()) - /// Space: O(t.size()) - public func toArray(self : Text) : [Char] { - let cs = self.chars(); - // We rely on Array_tabulate's implementation details: it fills - // the array from left to right sequentially. - Prim.Array_tabulate( - self.size(), - func _ { - switch (cs.next()) { - case (?c) { c }; - case null { Prim.trap("Text.toArray()") } - } - } - ) - }; - - /// Creates a new mutable `Array` containing characters of the given `Text`. - /// - /// Equivalent to `Iter.toArrayMut(t.chars())`. - /// - /// ```motoko include=import - /// import VarArray "mo:core/VarArray"; - /// import Char "mo:core/Char"; - /// - /// assert VarArray.equal(Text.toVarArray("Café"), [var 'C', 'a', 'f', 'é'], Char.equal); - /// ``` - /// - /// Runtime: O(t.size()) - /// Space: O(t.size()) - public func toVarArray(self : Text) : [var Char] { - let n = self.size(); - if (n == 0) { - return [var] - }; - let array = Prim.Array_init(n, ' '); - var i = 0; - for (c in self.chars()) { - array[i] := c; - i += 1 - }; - array - }; - - /// Creates a `Text` value from a `Char` iterator. - /// - /// ```motoko include=import - /// let text = Text.fromIter(['a', 'b', 'c'].values()); - /// assert text == "abc"; - /// ``` - public func fromIter(cs : Iter.Iter) : Text { - var r = ""; - for (c in cs) { - r #= Prim.charToText(c) - }; - return r - }; - - /// Returns whether the given `Text` is empty (has a size of zero). - /// - /// ```motoko include=import - /// let text1 = ""; - /// let text2 = "example"; - /// assert Text.isEmpty(text1); - /// assert not Text.isEmpty(text2); - /// ``` - public func isEmpty(self : Text) : Bool = self == ""; - - /// Returns the number of characters in the given `Text`. - /// - /// Equivalent to calling `t.size()` where `t` is a `Text` value. - /// - /// ```motoko include=import - /// let size = Text.size("abc"); - /// assert size == 3; - /// ``` - public func size(self : Text) : Nat = self.size(); - - /// Returns `t1 # t2`, where `#` is the `Text` concatenation operator. - /// - /// ```motoko include=import - /// let a = "Hello"; - /// let b = "There"; - /// let together = a # b; - /// assert together == "HelloThere"; - /// let withSpace = a # " " # b; - /// assert withSpace == "Hello There"; - /// let togetherAgain = Text.concat(a, b); - /// assert togetherAgain == "HelloThere"; - /// ``` - public func concat(self : Text, other : Text) : Text = self # other; - - /// Returns a new `Text` with the characters of the input `Text` in reverse order. - /// - /// ```motoko include=import - /// let text = Text.reverse("Hello"); - /// assert text == "olleH"; - /// ``` - /// - /// Runtime: O(t.size()) - /// Space: O(t.size()) - public func reverse(self : Text) : Text { - fromIter(Iter.reverse(self.chars())) - }; - - /// Returns true if two text values are equal. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.equal("hello", "hello"); - /// assert not Text.equal("hello", "world"); - /// ``` - public func equal(self : Text, other : Text) : Bool { self == other }; - - /// Returns true if two text values are not equal. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.notEqual("hello", "world"); - /// assert not Text.notEqual("hello", "hello"); - /// ``` - public func notEqual(self : Text, other : Text) : Bool { self != other }; - - /// Returns true if the first text value is lexicographically less than the second. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.less("apple", "banana"); - /// assert not Text.less("banana", "apple"); - /// ``` - public func less(self : Text, other : Text) : Bool { self < other }; - - /// Returns true if the first text value is lexicographically less than or equal to the second. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.lessOrEqual("apple", "banana"); - /// assert Text.lessOrEqual("apple", "apple"); - /// assert not Text.lessOrEqual("banana", "apple"); - /// ``` - public func lessOrEqual(self : Text, other : Text) : Bool { self <= other }; - - /// Returns true if the first text value is lexicographically greater than the second. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.greater("banana", "apple"); - /// assert not Text.greater("apple", "banana"); - /// ``` - public func greater(self : Text, other : Text) : Bool { self > other }; - - /// Returns true if the first text value is lexicographically greater than or equal to the second. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.greaterOrEqual("banana", "apple"); - /// assert Text.greaterOrEqual("apple", "apple"); - /// assert not Text.greaterOrEqual("apple", "banana"); - /// ``` - public func greaterOrEqual(self : Text, other : Text) : Bool { self >= other }; - - /// Compares `t1` and `t2` lexicographically. - /// - /// ```motoko include=import - /// assert Text.compare("abc", "abc") == #equal; - /// assert Text.compare("abc", "def") == #less; - /// assert Text.compare("abc", "ABC") == #greater; - /// ``` - public func compare(self : Text, other : Text) : Order.Order { - let c = Prim.textCompare(self, other); - if (c < 0) #less else if (c == 0) #equal else #greater - }; - - private func extract(self : Text, i : Nat, j : Nat) : Text { - let size = self.size(); - if (i == 0 and j == size) return self; - assert (j <= size); - let cs = self.chars(); - var r = ""; - var n = i; - while (n > 0) { - ignore cs.next(); - n -= 1 - }; - n := j; - while (n > 0) { - switch (cs.next()) { - case null { assert false }; - case (?c) { r #= Prim.charToText(c) } - }; - n -= 1 - }; - return r - }; - - /// Join an iterator of `Text` values with a given delimiter. - /// - /// ```motoko include=import - /// let joined = Text.join(["a", "b", "c"].values(), ", "); - /// assert joined == "a, b, c"; - /// ``` - public func join(self : Iter.Iter, sep : Text) : Text { - var r = ""; - if (sep.size() == 0) { - for (t in self) { - r #= t - }; - return r - }; - let next = self.next; - switch (next()) { - case null { return r }; - case (?t) { - r #= t - } - }; - loop { - switch (next()) { - case null { return r }; - case (?t) { - r #= sep; - r #= t - } - } - } - }; - - /// Applies a function to each character in a `Text` value, returning the concatenated `Char` results. - /// - /// ```motoko include=import - /// // Replace all occurrences of '?' with '!' - /// let result = Text.map("Motoko?", func(c) { - /// if (c == '?') '!' - /// else c - /// }); - /// assert result == "Motoko!"; - /// ``` - public func map(self : Text, f : Char -> Char) : Text { - var r = ""; - for (c in self.chars()) { - r #= Prim.charToText(f(c)) - }; - r - }; - - /// Returns the result of applying `f` to each character in `ts`, concatenating the intermediate text values. - /// - /// ```motoko include=import - /// // Replace all occurrences of '?' with "!!" - /// let result = Text.flatMap("Motoko?", func(c) { - /// if (c == '?') "!!" - /// else Text.fromChar(c) - /// }); - /// assert result == "Motoko!!"; - /// ``` - public func flatMap(self : Text, f : Char -> Text) : Text { - var r = ""; - for (c in self.chars()) { - r #= f(c) - }; - r - }; - - /// A pattern `p` describes a sequence of characters. A pattern has one of the following forms: - /// - /// * `#char c` matches the single character sequence, `c`. - /// * `#text t` matches multi-character text sequence `t`. - /// * `#predicate p` matches any single character sequence `c` satisfying predicate `p(c)`. - /// - /// A _match_ for `p` is any sequence of characters matching the pattern `p`. - /// - /// ```motoko include=import - /// let charPattern = #char 'A'; - /// let textPattern = #text "phrase"; - /// let predicatePattern : Text.Pattern = #predicate (func(c) { c == 'A' or c == 'B' }); - /// assert Text.contains("A", predicatePattern); - /// assert Text.contains("B", predicatePattern); - /// ``` - public type Pattern = Types.Pattern; - - private func take(n : Nat, cs : Iter.Iter) : Iter.Iter { - var i = n; - object { - public func next() : ?Char { - if (i == 0) return null; - i -= 1; - return cs.next() - } - } - }; - - private func empty() : Iter.Iter { - object { - public func next() : ?Char = null - } - }; - - private type Match = { - /// #success on complete match - #success; - /// #fail(cs,c) on partial match of cs, but failing match on c - #fail : (cs : Iter.Iter, c : Char); - /// #empty(cs) on partial match of cs and empty stream - #empty : (cs : Iter.Iter) - }; - - private func sizeOfPattern(pat : Pattern) : Nat { - switch pat { - case (#text(t)) { t.size() }; - case (#predicate(_) or #char(_)) { 1 } - } - }; - - private func matchOfPattern(pat : Pattern) : (cs : Iter.Iter) -> Match { - switch pat { - case (#char(p)) { - func(cs : Iter.Iter) : Match { - switch (cs.next()) { - case (?c) { - if (p == c) { - #success - } else { - #fail(empty(), c) - } - }; - case null { #empty(empty()) } - } - } - }; - case (#predicate(p)) { - func(cs : Iter.Iter) : Match { - switch (cs.next()) { - case (?c) { - if (p(c)) { - #success - } else { - #fail(empty(), c) - } - }; - case null { #empty(empty()) } - } - } - }; - case (#text(p)) { - func(cs : Iter.Iter) : Match { - var i = 0; - let ds = p.chars(); - loop { - switch (ds.next()) { - case (?d) { - switch (cs.next()) { - case (?c) { - if (c != d) { - return #fail(take(i, p.chars()), c) - }; - i += 1 - }; - case null { - return #empty(take(i, p.chars())) - } - } - }; - case null { return #success } - } - } - } - } - } - }; - - private class CharBuffer(cs : Iter.Iter) : Iter.Iter = { - - var stack : Stack.Stack<(Iter.Iter, Char)> = Stack.empty(); - - public func pushBack(cs0 : Iter.Iter, c : Char) { - Stack.push(stack, (cs0, c)) - }; - - public func next() : ?Char { - switch (Stack.peek(stack)) { - case (?(buff, c)) { - switch (buff.next()) { - case null { - ignore Stack.pop(stack); - return ?c - }; - case oc { - return oc - } - } - }; - case null { - return cs.next() - } - } - } - }; - - /// Splits the input `Text` with the specified `Pattern`. - /// - /// Two fields are separated by exactly one match. - /// - /// ```motoko include=import - /// let words = Text.split("This is a sentence.", #char ' '); - /// assert Text.join(words, "|") == "This|is|a|sentence."; - /// ``` - public func split(self : Text, p : Pattern) : Iter.Iter { - let match = matchOfPattern(p); - let cs = CharBuffer(self.chars()); - var state = 0; - var field = ""; - object { - public func next() : ?Text { - switch state { - case (0 or 1) { - loop { - switch (match(cs)) { - case (#success) { - let r = field; - field := ""; - state := 1; - return ?r - }; - case (#empty(cs1)) { - for (c in cs1) { - field #= fromChar(c) - }; - let r = if (state == 0 and field == "") { - null - } else { - ?field - }; - state := 2; - return r - }; - case (#fail(cs1, c)) { - cs.pushBack(cs1, c); - switch (cs.next()) { - case (?ci) { - field #= fromChar(ci) - }; - case null { - let r = if (state == 0 and field == "") { - null - } else { - ?field - }; - state := 2; - return r - } - } - } - } - } - }; - case _ { return null } - } - } - } - }; - - /// Returns a sequence of tokens from the input `Text` delimited by the specified `Pattern`, derived from start to end. - /// A "token" is a non-empty maximal subsequence of `t` not containing a match for pattern `p`. - /// Two tokens may be separated by one or more matches of `p`. - /// - /// ```motoko include=import - /// let tokens = Text.tokens("this needs\n an example", #predicate (func(c) { c == ' ' or c == '\n' })); - /// assert Text.join(tokens, "|") == "this|needs|an|example"; - /// ``` - public func tokens(self : Text, p : Pattern) : Iter.Iter { - let fs = split(self, p); - object { - public func next() : ?Text { - switch (fs.next()) { - case (?"") { next() }; - case ot { ot } - } - } - } - }; - - /// Returns `true` if the input `Text` contains a match for the specified `Pattern`. - /// - /// ```motoko include=import - /// assert Text.contains("Motoko", #text "oto"); - /// assert not Text.contains("Motoko", #text "xyz"); - /// ``` - public func contains(self : Text, p : Pattern) : Bool { - let match = matchOfPattern(p); - let cs = CharBuffer(self.chars()); - loop { - switch (match(cs)) { - case (#success) { - return true - }; - case (#empty(_cs1)) { - return false - }; - case (#fail(cs1, c)) { - cs.pushBack(cs1, c); - switch (cs.next()) { - case null { - return false - }; - case _ {}; // continue - } - } - } - } - }; - - /// Returns `true` if the input `Text` starts with a prefix matching the specified `Pattern`. - /// - /// ```motoko include=import - /// assert Text.startsWith("Motoko", #text "Mo"); - /// ``` - public func startsWith(self : Text, p : Pattern) : Bool { - var cs = self.chars(); - let match = matchOfPattern(p); - switch (match(cs)) { - case (#success) { true }; - case _ { false } - } - }; - - /// Returns `true` if the input `Text` ends with a suffix matching the specified `Pattern`. - /// - /// ```motoko include=import - /// assert Text.endsWith("Motoko", #char 'o'); - /// ``` - public func endsWith(self : Text, p : Pattern) : Bool { - let s2 = sizeOfPattern(p); - if (s2 == 0) return true; - let s1 = self.size(); - if (s2 > s1) return false; - let match = matchOfPattern(p); - var cs1 = self.chars(); - var diff : Nat = s1 - s2; - while (diff > 0) { - ignore cs1.next(); - diff -= 1 - }; - switch (match(cs1)) { - case (#success) { true }; - case _ { false } - } - }; - - /// Returns the input text `t` with all matches of pattern `p` replaced by text `r`. - /// - /// ```motoko include=import - /// let result = Text.replace("abcabc", #char 'a', "A"); - /// assert result == "AbcAbc"; - /// ``` - public func replace(self : Text, p : Pattern, r : Text) : Text { - let match = matchOfPattern(p); - let size = sizeOfPattern(p); - let cs = CharBuffer(self.chars()); - var res = ""; - label l loop { - switch (match(cs)) { - case (#success) { - res #= r; - if (size > 0) { - continue l - } - }; - case (#empty(cs1)) { - for (c1 in cs1) { - res #= fromChar(c1) - }; - break l - }; - case (#fail(cs1, c)) { - cs.pushBack(cs1, c) - } - }; - switch (cs.next()) { - case null { - break l - }; - case (?c1) { - res #= fromChar(c1) - }; // continue - } - }; - return res - }; - - /// Strips one occurrence of the given `Pattern` from the beginning of the input `Text`. - /// If you want to remove multiple instances of the pattern, use `Text.trimStart()` instead. - /// - /// ```motoko include=import - /// // Try to strip a nonexistent character - /// let none = Text.stripStart("abc", #char '-'); - /// assert none == null; - /// // Strip just one '-' - /// let one = Text.stripStart("--abc", #char '-'); - /// assert one == ?"-abc"; - /// ``` - public func stripStart(self : Text, p : Pattern) : ?Text { - let s = sizeOfPattern(p); - if (s == 0) return ?self; - var cs = self.chars(); - let match = matchOfPattern(p); - switch (match(cs)) { - case (#success) return ?fromIter(cs); - case _ return null - } - }; - - /// Strips one occurrence of the given `Pattern` from the end of the input `Text`. - /// If you want to remove multiple instances of the pattern, use `Text.trimEnd()` instead. - /// - /// ```motoko include=import - /// // Try to strip a nonexistent character - /// let none = Text.stripEnd("xyz", #char '-'); - /// assert none == null; - /// // Strip just one '-' - /// let one = Text.stripEnd("xyz--", #char '-'); - /// assert one == ?"xyz-"; - /// ``` - public func stripEnd(self : Text, p : Pattern) : ?Text { - let s2 = sizeOfPattern(p); - if (s2 == 0) return ?self; - let s1 = self.size(); - if (s2 > s1) return null; - let match = matchOfPattern(p); - var cs1 = self.chars(); - var diff : Nat = s1 - s2; - while (diff > 0) { - ignore cs1.next(); - diff -= 1 - }; - switch (match(cs1)) { - case (#success) return ?extract(self, 0, s1 - s2); - case _ return null - } - }; - - /// Trims the given `Pattern` from the start of the input `Text`. - /// If you only want to remove a single instance of the pattern, use `Text.stripStart()` instead. - /// - /// ```motoko include=import - /// let trimmed = Text.trimStart("---abc", #char '-'); - /// assert trimmed == "abc"; - /// ``` - public func trimStart(self : Text, p : Pattern) : Text { - let cs = self.chars(); - let size = sizeOfPattern(p); - if (size == 0) return self; - var matchSize = 0; - let match = matchOfPattern(p); - loop { - switch (match(cs)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(cs1)) { - return if (matchSize == 0) { - self - } else { - fromIter(cs1) - } - }; - case (#fail(cs1, c)) { - return if (matchSize == 0) { - self - } else { - fromIter(cs1) # fromChar(c) # fromIter(cs) - } - } - } - } - }; - - /// Trims the given `Pattern` from the end of the input `Text`. - /// If you only want to remove a single instance of the pattern, use `Text.stripEnd()` instead. - /// - /// ```motoko include=import - /// let trimmed = Text.trimEnd("xyz---", #char '-'); - /// assert trimmed == "xyz"; - /// ``` - public func trimEnd(self : Text, p : Pattern) : Text { - let cs = CharBuffer(self.chars()); - let size = sizeOfPattern(p); - if (size == 0) return self; - let match = matchOfPattern(p); - var matchSize = 0; - label l loop { - switch (match(cs)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(cs1)) { - switch (cs1.next()) { - case null break l; - case (?_) return self - } - }; - case (#fail(cs1, c)) { - matchSize := 0; - cs.pushBack(cs1, c); - ignore cs.next() - } - } - }; - extract(self, 0, self.size() - matchSize) - }; - - /// Trims the given `Pattern` from both the start and end of the input `Text`. - /// - /// ```motoko include=import - /// let trimmed = Text.trim("---abcxyz---", #char '-'); - /// assert trimmed == "abcxyz"; - /// ``` - public func trim(self : Text, p : Pattern) : Text { - let cs = self.chars(); - let size = sizeOfPattern(p); - if (size == 0) return self; - var matchSize = 0; - let match = matchOfPattern(p); - loop { - switch (match(cs)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(cs1)) { - return if (matchSize == 0) { self } else { fromIter(cs1) } - }; - case (#fail(cs1, c)) { - let start = matchSize; - let cs2 = CharBuffer(cs); - cs2.pushBack(cs1, c); - ignore cs2.next(); - matchSize := 0; - label l loop { - switch (match(cs2)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(_cs3)) { - switch (cs1.next()) { - case null break l; - case (?_) return self - } - }; - case (#fail(cs3, c1)) { - matchSize := 0; - cs2.pushBack(cs3, c1); - ignore cs2.next() - } - } - }; - return extract(self, start, self.size() - matchSize - start) - } - } - } - }; - - /// Compares `t1` and `t2` using the provided character-wise comparison function. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// assert Text.compareWith("abc", "ABC", func(c1, c2) { Char.compare(c1, c2) }) == #greater; - /// ``` - public func compareWith( - self : Text, - other : Text, - compare : (Char, Char) -> Order.Order - ) : Order.Order { - let cs1 = self.chars(); - let cs2 = other.chars(); - loop { - switch (cs1.next(), cs2.next()) { - case (null, null) { return #equal }; - case (null, ?_) { return #less }; - case (?_, null) { return #greater }; - case (?c1, ?c2) { - switch (compare(c1, c2)) { - case (#equal) {}; // continue - case other { return other } - } - } - } - } - }; - - /// Returns a UTF-8 encoded `Blob` from the given `Text`. - /// - /// ```motoko include=import - /// let blob = Text.encodeUtf8("Hello"); - /// assert blob == "\48\65\6C\6C\6F"; - /// ``` - public let encodeUtf8 : (self : Text) -> Blob = Prim.encodeUtf8; - - /// Tries to decode the given `Blob` as UTF-8. - /// Returns `null` if the blob is not valid UTF-8. - /// - /// ```motoko include=import - /// let text = Text.decodeUtf8("\48\65\6C\6C\6F"); - /// assert text == ?"Hello"; - /// ``` - public let decodeUtf8 : (self : Blob) -> ?Text = Prim.decodeUtf8; - - /// Returns the text argument in lowercase. - /// WARNING: Unicode compliant only when compiled, not interpreted. - /// - /// ```motoko include=import - /// let text = Text.toLower("Good Day"); - /// assert text == "good day"; - /// ``` - public let toLower : (self : Text) -> Text = Prim.textLowercase; - - /// Returns the text argument in uppercase. Unicode compliant. - /// WARNING: Unicode compliant only when compiled, not interpreted. - /// - /// ```motoko include=import - /// let text = Text.toUpper("Good Day"); - /// assert text == "GOOD DAY"; - /// ``` - public let toUpper : (self : Text) -> Text = Prim.textUppercase; - - /// Returns the given text value unchanged. - /// This function is provided for consistency with other modules. - /// - /// ```motoko include=import - /// assert Text.toText("Hello") == "Hello"; - /// ``` - public func toText(self : Text) : Text = self - -} diff --git a/.mops/core@2.4.0/src/Time.mo b/.mops/core@2.4.0/src/Time.mo deleted file mode 100644 index 00197a7..0000000 --- a/.mops/core@2.4.0/src/Time.mo +++ /dev/null @@ -1,62 +0,0 @@ -/// System time utilities and timers. -/// -/// The following example illustrates using the system time: -/// -/// ```motoko -/// import Int = "mo:core/Int"; -/// import Time = "mo:core/Time"; -/// -/// persistent actor { -/// var lastTime = Time.now(); -/// -/// public func greet(name : Text) : async Text { -/// let now = Time.now(); -/// let elapsedSeconds = (now - lastTime) / 1000_000_000; -/// lastTime := now; -/// return "Hello, " # name # "!" # -/// " I was last called " # Int.toText(elapsedSeconds) # " seconds ago"; -/// }; -/// }; -/// ``` -/// -/// Note: If `moc` is invoked with `-no-timer`, the importing will fail. -/// Note: The resolution of the timers is in the order of the block rate, -/// so durations should be chosen well above that. For frequent -/// canister wake-ups the heartbeat mechanism should be considered. - -import Types "Types"; -import Nat "Nat"; -import Prim "mo:⛔"; - -module { - - /// System time is represent as nanoseconds since 1970-01-01. - public type Time = Types.Time; - - /// Quantity of time expressed in `#days`, `#hours`, `#minutes`, `#seconds`, `#milliseconds`, or `#nanoseconds`. - public type Duration = Types.Duration; - - /// Current system time given as nanoseconds since 1970-01-01. The system guarantees that: - /// - /// * the time, as observed by the canister smart contract, is monotonically increasing, even across canister upgrades. - /// * within an invocation of one entry point, the time is constant. - /// - /// The system times of different canisters are unrelated, and calls from one canister to another may appear to travel "backwards in time" - /// - /// Note: While an implementation will likely try to keep the system time close to the real time, this is not formally guaranteed. - public func now() : Time = Prim.nat64ToNat(Prim.time()); - - public type TimerId = Nat; - - public func toNanoseconds(duration : Duration) : Nat { - switch duration { - case (#days n) n * 86_400_000_000_000; - case (#hours n) n * 3_600_000_000_000; - case (#minutes n) n * 60_000_000_000; - case (#seconds n) n * 1_000_000_000; - case (#milliseconds n) n * 1_000_000; - case (#nanoseconds n) n - } - }; - -} diff --git a/.mops/core@2.4.0/src/Timer.mo b/.mops/core@2.4.0/src/Timer.mo deleted file mode 100644 index 6f4f377..0000000 --- a/.mops/core@2.4.0/src/Timer.mo +++ /dev/null @@ -1,84 +0,0 @@ -/// Timers for one-off or periodic tasks. Applicable as part of the default mechanism. -/// If `moc` is invoked with `-no-timer`, the importing will fail. Furthermore, if passed `--trap-on-call-error`, a congested canister send queue may prevent timer expirations to execute at runtime. It may also deactivate the global timer. -/// -/// ```motoko name=import -/// import Timer "mo:core/Timer"; -/// ``` -/// -/// The resolution of the timers is similar to the block rate, -/// so durations should be chosen well above that. For frequent -/// canister wake-ups, consider using the [heartbeat](https://internetcomputer.org/docs/motoko/icp-features/system-functions#heartbeat) mechanism; however, when possible, canisters should prefer timers. -/// -/// The functionality described below is enabled only when the actor does not override it by declaring an explicit `system func timer`. -/// -/// Timers are _not_ persisted across upgrades. One possible strategy -/// to re-establish timers after an upgrade is to use stable variables -/// in the `post_upgrade` hook and distill necessary timer information -/// from there. -/// -/// Using timers for security (e.g., access control) is strongly discouraged. -/// Make sure to inform yourself about state-of-the-art dapp security. -/// If you must use timers for security controls, be sure -/// to consider reentrancy issues as well as the vanishing of timers on upgrades -/// and reinstalls. -/// -/// For further usage information for timers on the IC, please consult -/// [the documentation](https://internetcomputer.org/docs/building-apps/network-features/periodic-tasks-timers#timers-library-limitations). -import { setTimer = setTimerNano; cancelTimer = cancel } = "mo:⛔"; -import Nat64 = "Nat64"; -import Time "Time"; - -module { - - public type TimerId = Nat; - - /// Installs a one-off timer that upon expiration after given duration `d` - /// executes the future `job()`. - /// - /// ```motoko include=import no-repl - /// import Int "mo:core/Int"; - /// - /// func runIn30Minutes() : async () { - /// // ... - /// }; - /// let timerId = Timer.setTimer(#minutes 30, runIn30Minutes); - /// ``` - public func setTimer(duration : Time.Duration, job : () -> async ()) : TimerId { - setTimerNano(Nat64.fromNat(Time.toNanoseconds duration), false, job) - }; - - /// Installs a recurring timer that upon expiration after given duration `d` - /// executes the future `job()` and reinserts itself for another expiration. - /// - /// Note: A duration of 0 will only expire once. - /// - /// ```motoko include=import no-repl - /// func runEvery30Minutes() : async () { - /// // ... - /// }; - /// let timerId = Timer.recurringTimer(#minutes 30, runEvery30Minutes); - /// ``` - public func recurringTimer(duration : Time.Duration, job : () -> async ()) : TimerId { - setTimerNano(Nat64.fromNat(Time.toNanoseconds duration), true, job) - }; - - /// Cancels a still active timer with `(id : TimerId)`. For expired timers - /// and not recognised `id`s nothing happens. - /// - /// ```motoko include=import no-repl - /// var counter = 0; - /// var timerId : ?Timer.TimerId = null; - /// func runFiveTimes() : async () { - /// counter += 1; - /// if (counter == 5) { - /// switch (timerId) { - /// case (?id) { Timer.cancelTimer(id) }; - /// case null { assert false /* timer already cancelled */ }; - /// }; - /// } - /// }; - /// timerId := ?Timer.recurringTimer(#minutes 30, runFiveTimes); - /// ``` - public let cancelTimer : TimerId -> () = cancel; - -} diff --git a/.mops/core@2.4.0/src/Tuples.mo b/.mops/core@2.4.0/src/Tuples.mo deleted file mode 100644 index 89c5d6c..0000000 --- a/.mops/core@2.4.0/src/Tuples.mo +++ /dev/null @@ -1,365 +0,0 @@ -/// Contains modules for working with tuples of different sizes. -/// -/// Usage example: -/// -/// ```motoko -/// import { Tuple2; Tuple3 } "mo:core/Tuples"; -/// import Bool "mo:core/Bool"; -/// import Nat "mo:core/Nat"; -/// -/// let swapped = Tuple2.swap((1, "hello")); -/// assert swapped == ("hello", 1); -/// let text = Tuple3.toText((1, true, 3), Nat.toText, Bool.toText, Nat.toText); -/// assert text == "(1, true, 3)"; -/// ``` - -import Types "Types"; - -module { - - public module Tuple2 { - /// Swaps the elements of a tuple. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// - /// assert Tuple2.swap((1, "hello")) == ("hello", 1); - /// ``` - public func swap((a, b) : (A, B)) : (B, A) = (b, a); - - /// Creates a textual representation of a tuple for debugging purposes. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// assert Tuple2.toText((1, "hello"), Nat.toText, func (x: Text): Text = x) == "(1, hello)"; - /// ``` - public func toText( - self : (A, B), - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)) - ) : Text = "(" # toTextA(self.0) # ", " # toTextB(self.1) # ")"; - - /// Compares two tuples for equality. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple2.equal((1, "hello"), (1, "hello"), Nat.equal, Text.equal); - /// ``` - public func equal( - self : (A, B), - other : (A, B), - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)) - ) : Bool = equalA(self.0, other.0) and equalB(self.1, other.1); - - /// Compares two tuples lexicographically. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple2.compare((1, "hello"), (1, "world"), Nat.compare, Text.compare) == #less; - /// assert Tuple2.compare((1, "hello"), (2, "hello"), Nat.compare, Text.compare) == #less; - /// assert Tuple2.compare((1, "hello"), (1, "hello"), Nat.compare, Text.compare) == #equal; - /// assert Tuple2.compare((2, "hello"), (1, "hello"), Nat.compare, Text.compare) == #greater; - /// assert Tuple2.compare((1, "world"), (1, "hello"), Nat.compare, Text.compare) == #greater; - /// ``` - public func compare( - self : (A, B), - other : (A, B), - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)) - ) : Types.Order = switch (compareA(self.0, other.0)) { - case (#equal) compareB(self.1, other.1); - case order order - }; - - /// Creates a `toText` function for a tuple given `toText` functions for its elements. - /// This is useful when you need to reuse the same toText conversion multiple times. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// import Nat "mo:core/Nat"; - /// - /// let tupleToText = Tuple2.makeToText(Nat.toText, func x = x); - /// assert tupleToText((1, "hello")) == "(1, hello)"; - /// ``` - public func makeToText( - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)) - ) : ((A, B)) -> Text = func t = toText(t, toTextA, toTextB); - - /// Creates an `equal` function for a tuple given `equal` functions for its elements. - /// This is useful when you need to reuse the same equality comparison multiple times. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let tupleEqual = Tuple2.makeEqual(Nat.equal, Text.equal); - /// assert tupleEqual((1, "hello"), (1, "hello")); - /// ``` - public func makeEqual( - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)) - ) : ((A, B), (A, B)) -> Bool = func(t1, t2) = equal(t1, t2, equalA, equalB); - - /// Creates a `compare` function for a tuple given `compare` functions for its elements. - /// This is useful when you need to reuse the same comparison multiple times. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let tupleCompare = Tuple2.makeCompare(Nat.compare, Text.compare); - /// assert tupleCompare((1, "hello"), (1, "world")) == #less; - /// ``` - public func makeCompare( - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)) - ) : ((A, B), (A, B)) -> Types.Order = func(t1, t2) = compare(t1, t2, compareA, compareB) - }; - - public module Tuple3 { - /// Creates a textual representation of a 3-tuple for debugging purposes. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// assert Tuple3.toText((1, "hello", 2), Nat.toText, func (x: Text): Text = x, Nat.toText) == "(1, hello, 2)"; - /// ``` - public func toText( - self : (A, B, C), - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)), - toTextC : (implicit : (toText : C -> Text)) - ) : Text = "(" # toTextA(self.0) # ", " # toTextB(self.1) # ", " # toTextC(self.2) # ")"; - - /// Compares two 3-tuples for equality. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple3.equal((1, "hello", 2), (1, "hello", 2), Nat.equal, Text.equal, Nat.equal); - /// ``` - public func equal( - self : (A, B, C), - other : (A, B, C), - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)), - equalC : (implicit : (equal : (C, C) -> Bool)) - ) : Bool = equalA(self.0, other.0) and equalB(self.1, other.1) and equalC(self.2, other.2); - - /// Compares two 3-tuples lexicographically. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple3.compare((1, "hello", 2), (1, "world", 1), Nat.compare, Text.compare, Nat.compare) == #less; - /// assert Tuple3.compare((1, "hello", 2), (2, "hello", 2), Nat.compare, Text.compare, Nat.compare) == #less; - /// assert Tuple3.compare((1, "hello", 2), (1, "hello", 2), Nat.compare, Text.compare, Nat.compare) == #equal; - /// assert Tuple3.compare((2, "hello", 2), (1, "hello", 2), Nat.compare, Text.compare, Nat.compare) == #greater; - /// ``` - public func compare( - self : (A, B, C), - other : (A, B, C), - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)), - compareC : (implicit : (compare : (C, C) -> Types.Order)) - ) : Types.Order = switch (compareA(self.0, other.0)) { - case (#equal) { - switch (compareB(self.1, other.1)) { - case (#equal) compareC(self.2, other.2); - case order order - } - }; - case order order - }; - - /// Creates a `toText` function for a 3-tuple given `toText` functions for its elements. - /// This is useful when you need to reuse the same toText conversion multiple times. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// let toText = Tuple3.makeToText(Nat.toText, func x = x, Nat.toText); - /// assert toText((1, "hello", 2)) == "(1, hello, 2)"; - /// ``` - public func makeToText( - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)), - toTextC : (implicit : (toText : C -> Text)) - ) : ((A, B, C)) -> Text = func t = toText(t, toTextA, toTextB, toTextC); - - /// Creates an `equal` function for a 3-tuple given `equal` functions for its elements. - /// This is useful when you need to reuse the same equality comparison multiple times. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// let equal = Tuple3.makeEqual(Nat.equal, Text.equal, Nat.equal); - /// assert equal((1, "hello", 2), (1, "hello", 2)); - /// ``` - public func makeEqual( - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)), - equalC : (implicit : (equal : (C, C) -> Bool)) - ) : ((A, B, C), (A, B, C)) -> Bool = func(t1, t2) = equal(t1, t2, equalA, equalB, equalC); - - /// Creates a `compare` function for a 3-tuple given `compare` functions for its elements. - /// This is useful when you need to reuse the same comparison multiple times. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// let compare = Tuple3.makeCompare(Nat.compare, Text.compare, Nat.compare); - /// assert compare((1, "hello", 2), (1, "world", 1)) == #less; - /// ``` - public func makeCompare( - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)), - compareC : (implicit : (compare : (C, C) -> Types.Order)) - ) : ((A, B, C), (A, B, C)) -> Types.Order = func(t1, t2) = compare(t1, t2, compareA, compareB, compareC) - }; - - public module Tuple4 { - /// Creates a textual representation of a 4-tuple for debugging purposes. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// assert Tuple4.toText((1, "hello", 2, 3), Nat.toText, func (x: Text): Text = x, Nat.toText, Nat.toText) == "(1, hello, 2, 3)"; - /// ``` - public func toText( - self : (A, B, C, D), - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)), - toTextC : (implicit : (toText : C -> Text)), - toTextD : (implicit : (toText : D -> Text)) - ) : Text = "(" # toTextA(self.0) # ", " # toTextB(self.1) # ", " # toTextC(self.2) # ", " # toTextD(self.3) # ")"; - - /// Compares two 4-tuples for equality. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple4.equal((1, "hello", 2, 3), (1, "hello", 2, 3), Nat.equal, Text.equal, Nat.equal, Nat.equal); - /// ``` - public func equal( - self : (A, B, C, D), - other : (A, B, C, D), - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)), - equalC : (implicit : (equal : (C, C) -> Bool)), - equalD : (implicit : (equal : (D, D) -> Bool)) - ) : Bool = equalA(self.0, other.0) and equalB(self.1, other.1) and equalC(self.2, other.2) and equalD(self.3, other.3); - - /// Compares two 4-tuples lexicographically. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple4.compare((1, "hello", 2, 3), (1, "world", 1, 3), Nat.compare, Text.compare, Nat.compare, Nat.compare) == #less; - /// assert Tuple4.compare((1, "hello", 2, 3), (2, "hello", 2, 3), Nat.compare, Text.compare, Nat.compare, Nat.compare) == #less; - /// assert Tuple4.compare((1, "hello", 2, 3), (1, "hello", 2, 3), Nat.compare, Text.compare, Nat.compare, Nat.compare) == #equal; - /// assert Tuple4.compare((2, "hello", 2, 3), (1, "hello", 2, 3), Nat.compare, Text.compare, Nat.compare, Nat.compare) == #greater; - /// ``` - public func compare( - self : (A, B, C, D), - other : (A, B, C, D), - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)), - compareC : (implicit : (compare : (C, C) -> Types.Order)), - compareD : (implicit : (compare : (D, D) -> Types.Order)) - ) : Types.Order = switch (compareA(self.0, other.0)) { - case (#equal) { - switch (compareB(self.1, other.1)) { - case (#equal) { - switch (compareC(self.2, other.2)) { - case (#equal) compareD(self.3, other.3); - case order order - } - }; - case order order - } - }; - case order order - }; - - /// Creates a `toText` function for a 4-tuple given `toText` functions for its elements. - /// This is useful when you need to reuse the same toText conversion multiple times. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// let toText = Tuple4.makeToText(Nat.toText, func (x: Text): Text = x, Nat.toText, Nat.toText); - /// assert toText((1, "hello", 2, 3)) == "(1, hello, 2, 3)"; - /// ``` - public func makeToText( - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)), - toTextC : (implicit : (toText : C -> Text)), - toTextD : (implicit : (toText : D -> Text)) - ) : ((A, B, C, D)) -> Text = func t = toText(t, toTextA, toTextB, toTextC, toTextD); - - /// Creates an `equal` function for a 4-tuple given `equal` functions for its elements. - /// This is useful when you need to reuse the same equality comparison multiple times. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// let equal = Tuple4.makeEqual(Nat.equal, Text.equal, Nat.equal, Nat.equal); - /// assert equal((1, "hello", 2, 3), (1, "hello", 2, 3)); - /// ``` - public func makeEqual( - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)), - equalC : (implicit : (equal : (C, C) -> Bool)), - equalD : (implicit : (equal : (D, D) -> Bool)) - ) : ((A, B, C, D), (A, B, C, D)) -> Bool = func(t1, t2) = equal(t1, t2, equalA, equalB, equalC, equalD); - - /// Creates a `compare` function for a 4-tuple given `compare` functions for its elements. - /// This is useful when you need to reuse the same comparison multiple times. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// let compare = Tuple4.makeCompare(Nat.compare, Text.compare, Nat.compare, Nat.compare); - /// assert compare((1, "hello", 2, 3), (1, "world", 1, 3)) == #less; - /// ``` - public func makeCompare( - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)), - compareC : (implicit : (compare : (C, C) -> Types.Order)), - compareD : (implicit : (compare : (D, D) -> Types.Order)) - ) : ((A, B, C, D), (A, B, C, D)) -> Types.Order = func(t1, t2) = compare(t1, t2, compareA, compareB, compareC, compareD) - } -} diff --git a/.mops/core@2.4.0/src/Types.mo b/.mops/core@2.4.0/src/Types.mo deleted file mode 100644 index 195972f..0000000 --- a/.mops/core@2.4.0/src/Types.mo +++ /dev/null @@ -1,181 +0,0 @@ -/// Common types used throughout the core package. -/// -/// Example usage: -/// -/// ```motoko name=import -/// import { type Result; type Iter } "mo:core/Types"; -/// -/// // Result for error handling -/// let result : Result = #ok(42); -/// -/// // Iterator for sequences -/// let iter : Iter = { next = func() { ?1 } }; -/// ``` - -import Prim "mo:⛔"; - -module { - public type Blob = Prim.Types.Blob; - public type Bool = Prim.Types.Bool; - public type Char = Prim.Types.Char; - public type Error = Prim.Types.Error; - public type ErrorCode = Prim.ErrorCode; - public type Float = Prim.Types.Float; - public type Int = Prim.Types.Int; - public type Int8 = Prim.Types.Int8; - public type Int16 = Prim.Types.Int16; - public type Int32 = Prim.Types.Int32; - public type Int64 = Prim.Types.Int64; - public type Nat = Prim.Types.Nat; - public type Nat8 = Prim.Types.Nat8; - public type Nat16 = Prim.Types.Nat16; - public type Nat32 = Prim.Types.Nat32; - public type Nat64 = Prim.Types.Nat64; - public type Principal = Prim.Types.Principal; - public type Region = Prim.Types.Region; - public type Text = Prim.Types.Text; - - public type Hash = Nat32; - public type Iter = { next : () -> ?T }; - public type Order = { #less; #equal; #greater }; - public type Result = { #ok : T; #err : E }; - public type Pattern = { - #char : Char; - #text : Text; - #predicate : (Char -> Bool) - }; - public type Time = Int; - public type Duration = { - #days : Nat; - #hours : Nat; - #minutes : Nat; - #seconds : Nat; - #milliseconds : Nat; - #nanoseconds : Nat - }; - public type TimerId = Nat; - - public type List = { - var blocks : [var [var ?T]]; - var blockIndex : Nat; - var elementIndex : Nat - }; - - public module Queue { - public type Queue = { - var front : ?Node; - var back : ?Node; - var size : Nat - }; - - public type Node = { - value : T; - var next : ?Node; - var previous : ?Node - } - }; - public type Queue = Queue.Queue; - - public module PriorityQueue { - public type PriorityQueue = { - heap : List - } - }; - public type PriorityQueue = PriorityQueue.PriorityQueue; - - public module Set { - public type Node = { - #leaf : Leaf; - #internal : Internal - }; - - public type Data = { - elements : [var ?T]; - var count : Nat - }; - - public type Internal = { - data : Data; - children : [var ?Node] - }; - - public type Leaf = { - data : Data - }; - - public type Set = { - var root : Node; - var size : Nat - } - }; - public type Set = Set.Set; - - public module Map { - public type Node = { - #leaf : Leaf; - #internal : Internal - }; - - public type Data = { - kvs : [var ?(K, V)]; - var count : Nat - }; - - public type Internal = { - data : Data; - children : [var ?Node] - }; - - public type Leaf = { - data : Data - }; - - public type Map = { - var root : Node; - var size : Nat - } - }; - - public type Map = Map.Map; - - public module Stack { - public type Stack = { - var top : Pure.List; - var size : Nat - } - }; - public type Stack = Stack.Stack; - - public module Pure { - public type List = ?(T, List); - - public module Map { - public type Map = { - size : Nat; - root : Tree - }; - public type Tree = { - #red : (Tree, K, V, Tree); - #black : (Tree, K, V, Tree); - #leaf - }; - - }; - public type Map = Map.Map; - - public type Queue = (List, Nat, List); - - public module Set { - public type Tree = { - #red : (Tree, T, Tree); - #black : (Tree, T, Tree); - #leaf - }; - - public type Set = { size : Nat; root : Tree } - }; - - public type Set = Set.Set; - - } -} diff --git a/.mops/core@2.4.0/src/VarArray.mo b/.mops/core@2.4.0/src/VarArray.mo deleted file mode 100644 index 7dac8c0..0000000 --- a/.mops/core@2.4.0/src/VarArray.mo +++ /dev/null @@ -1,1407 +0,0 @@ -/// Provides extended utility functions on mutable Arrays (`[var]`). -/// -/// Note the difference between mutable (`[var]`) and immutable (`[]`) arrays. -/// Mutable arrays allow their elements to be modified after creation, while -/// immutable arrays are fixed once created. -/// -/// WARNING: If you are looking for a list that can grow and shrink in size, -/// it is recommended you use `List` for those purposes. -/// Arrays must be created with a fixed size. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import VarArray "mo:core/VarArray"; -/// ``` - -import Types "Types"; -import Order "Order"; -import Result "Result"; -import Option "Option"; -import Prim "mo:⛔"; -import InsertionSort "internal/SortHelper"; - -module { - let nat = Prim.nat32ToNat; - - /// Creates an empty mutable array (equivalent to `[var]`). - /// - /// ```motoko include=import - /// let array = VarArray.empty(); - /// assert array.size() == 0; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func empty() : [var T] = [var]; - - /// Creates a mutable array containing `item` repeated `size` times. - /// - /// ```motoko include=import - /// import Text "mo:core/Text"; - /// - /// let array = VarArray.repeat("Echo", 3); - /// assert VarArray.equal(array, [var "Echo", "Echo", "Echo"], Text.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func repeat(item : T, size : Nat) : [var T] = Prim.Array_init(size, item); - - /// Duplicates `array`, returning a shallow copy of the original. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array1 = [var 1, 2, 3]; - /// let array2 = VarArray.clone(array1); - /// array2[0] := 0; - /// assert VarArray.equal(array1, [var 1, 2, 3], Nat.equal); - /// assert VarArray.equal(array2, [var 0, 2, 3], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func clone(self : [var T]) : [var T] = Prim.Array_tabulateVar(self.size(), func i = self[i]); - - /// Creates a mutable array of size `size`. Each element at index i - /// is created by applying `generator` to i. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array : [var Nat] = VarArray.tabulate(4, func i = i * 2); - /// assert VarArray.equal(array, [var 0, 2, 4, 6], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `generator` runs in O(1) time and space. - public let tabulate : (size : Nat, generator : Nat -> T) -> [var T] = Prim.Array_tabulateVar; - - /// Tests if two arrays contain equal values (i.e. they represent the same - /// list of elements). Uses `equal` to compare elements in the arrays. - /// - /// ```motoko include=import - /// // Use the equal function from the Nat module to compare Nats - /// import Nat "mo:core/Nat"; - /// - /// let array1 = [var 0, 1, 2, 3]; - /// let array2 = [var 0, 1, 2, 3]; - /// assert VarArray.equal(array1, array2, Nat.equal); - /// ``` - /// - /// Runtime: O(size1 + size2) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func equal(self : [var T], other : [var T], equal : (implicit : (T, T) -> Bool)) : Bool { - let size1 = self.size(); - let size2 = other.size(); - if (size1 != size2) { - return false - }; - var i = 0; - while (i < size1) { - if (not equal(self[i], other[i])) { - return false - }; - i += 1 - }; - true - }; - - /// Returns the first value in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let array = [var 1, 9, 4, 8]; - /// let found = VarArray.find(array, func x = x > 8); - /// assert found == ?9; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func find(self : [var T], predicate : T -> Bool) : ?T { - for (element in self.vals()) { - if (predicate element) { - return ?element - } - }; - null - }; - - /// Returns the first index in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let array = [var 'A', 'B', 'C', 'D']; - /// let found = VarArray.findIndex(array, func(x) { x == 'C' }); - /// assert found == ?2; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func findIndex(self : [var T], predicate : T -> Bool) : ?Nat { - for ((index, element) in enumerate(self)) { - if (predicate element) { - return ?index - } - }; - null - }; - - /// Create a new mutable array by concatenating the values of `array1` and `array2`. - /// Note that `VarArray.concat` copies its arguments and has linear complexity. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array1 = [var 1, 2, 3]; - /// let array2 = [var 4, 5, 6]; - /// let result = VarArray.concat(array1, array2); - /// assert VarArray.equal(result, [var 1, 2, 3, 4, 5, 6], Nat.equal); - /// ``` - /// Runtime: O(size1 + size2) - /// - /// Space: O(size1 + size2) - public func concat(self : [var T], other : [var T]) : [var T] { - let size1 = self.size(); - let size2 = other.size(); - tabulate( - size1 + size2, - func i { - if (i < size1) { - self[i] - } else { - other[i - size1] - } - } - ) - }; - - /// Creates a new sorted copy of the mutable array according to `compare`. - /// Sort is deterministic and stable. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 4, 2, 6]; - /// let sorted = VarArray.sort(array, Nat.compare); - /// assert VarArray.equal(sorted, [var 2, 4, 6], Nat.equal); - /// ``` - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sort(self : [var T], compare : (implicit : (T, T) -> Order.Order)) : [var T] { - let newArray = clone(self); - sortInPlace(newArray, compare); - newArray - }; - - /// Sorts the elements in a mutable array in place according to `compare`. - /// Sort is deterministic and stable. This modifies the original array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 4, 2, 6]; - /// VarArray.sortInPlace(array, Nat.compare); - /// assert VarArray.equal(array, [var 2, 4, 6], Nat.equal); - /// ``` - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sortInPlace(self : [var T], compare : (implicit : (T, T) -> Order.Order)) : () { - let size = Prim.natToNat32(self.size()); - if (size <= 1) return; - if (size <= 8) { - InsertionSort.insertionSortSmall(self, self, compare, 0 : Nat32, size); - return - }; - let buffer = repeat(self[0], nat(size / 2)); - mergeSortRec(self, buffer, compare, 0 : Nat32, size, true, 0 : Nat32) - }; - - // input data is alwways in array - // even: write output data to array in place - // odd: write output data to buffer at offset - // offset is only used when odd - func mergeSortRec( - array : [var T], - buffer : [var T], - compare : (T, T) -> Order.Order, - from : Nat32, - to : Nat32, - even : Bool, - offset : Nat32 - ) { - debug assert from < to; - let size = to -% from; - debug assert size >= 4; - - if (size <= 8) { - if (even) { - InsertionSort.insertionSortSmall(array, array, compare, from, size); // sorts array in place - } else { - InsertionSort.insertionSortSmallMove(array, buffer, compare, from, size, offset); // sorts to buffer at offset - }; - return - }; - - let len1 = size / 2; - let mid = from +% len1; - if (even) { - // merge to array in place - mergeSortRec(array, buffer, compare, mid, to, true, 0 : Nat32); // sort upper half to array in place - mergeSortRec(array, buffer, compare, from, mid, false, 0 : Nat32); // sort lower half to beginning of buffer - merge1(array, buffer, compare, from, mid, to); // merge to array in place - } else { - // merge to buffer at offset - mergeSortRec(array, buffer, compare, from, mid, true, 0 : Nat32); // lower half to array in place - mergeSortRec(array, buffer, compare, mid, to, false, offset +% len1); // sort upper half to buffer starting shifted offset - merge2(array, buffer, compare, from, mid, size, offset); // merge to buffer at offset - } - }; - - func merge1(array : [var T], buffer : [var T], compare : (T, T) -> Order.Order, from : Nat32, mid : Nat32, to : Nat32) { - debug assert from < mid; - debug assert mid < to; - let len = mid -% from; - var pos = from; - var i = 0 : Nat32; - var j = mid; - - var iElem = buffer[nat(i)]; - var jElem = array[nat(j)]; - label L loop { - switch (compare(jElem, iElem)) { - case (#less) { - array[nat(pos)] := jElem; - j +%= 1; - pos +%= 1; - if (j == to) { - while (i < len) { - array[nat(pos)] := buffer[nat(i)]; - i +%= 1; - pos +%= 1 - }; - break L - }; - jElem := array[nat(j)] - }; - case (_) { - array[nat(pos)] := iElem; - i +%= 1; - pos +%= 1; - if (i == len) break L; - iElem := buffer[nat(i)] - } - } - } - }; - - func merge2(array : [var T], buffer : [var T], compare : (T, T) -> Order.Order, from : Nat32, mid : Nat32, size : Nat32, offset : Nat32) { - debug assert from < mid; - debug assert mid < from +% size; - let len = mid -% from; - var pos = offset; - var i = from; - var j = offset +% len; - let j_max = offset +% size; - - var iElem = array[nat(i)]; - var jElem = buffer[nat(j)]; - label L loop { - switch (compare(jElem, iElem)) { - case (#less) { - buffer[nat(pos)] := jElem; - j +%= 1; - pos +%= 1; - if (j == j_max) { - while (i < mid) { - buffer[nat(pos)] := array[nat(i)]; - i +%= 1; - pos +%= 1 - }; - break L - }; - jElem := buffer[nat(j)] - }; - case (_) { - buffer[nat(pos)] := iElem; - i +%= 1; - pos +%= 1; - if (i == mid) break L; - iElem := array[nat(i)] - } - } - } - }; - - /// Creates a new mutable array by reversing the order of elements in `array`. - /// The original array is not modified. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 10, 11, 12]; - /// let reversed = VarArray.reverse(array); - /// assert VarArray.equal(reversed, [var 12, 11, 10], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func reverse(self : [var T]) : [var T] { - let size = self.size(); - tabulate(size, func i = self[size - i - 1]) - }; - - /// Reverses the order of elements in a mutable array in place. - /// This modifies the original array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 10, 11, 12]; - /// VarArray.reverseInPlace(array); - /// assert VarArray.equal(array, [var 12, 11, 10], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func reverseInPlace(self : [var T]) : () { - let size = self.size(); - if (size == 0) { - return - }; - var i = 0; - var j = (size - 1) : Nat; - while (i < j) { - let temp = self[i]; - self[i] := self[j]; - self[j] := temp; - i += 1; - j -= 1 - } - }; - - /// Calls `f` with each element in `array`. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// var sum = 0; - /// let array = [var 0, 1, 2, 3]; - /// VarArray.forEach(array, func(x) { - /// sum += x; - /// }); - /// assert sum == 6; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEach(self : [var T], f : T -> ()) { - for (item in self.vals()) { - f(item) - } - }; - - /// Creates a new mutable array by applying `f` to each element in `array`. `f` "maps" - /// each element it is applied to of type `T` to an element of type `R`. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 0, 1, 2, 3]; - /// let array2 = VarArray.map(array, func x = x * 2); - /// assert VarArray.equal(array2, [var 0, 2, 4, 6], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func map(self : [var T], f : T -> R) : [var R] { - tabulate( - self.size(), - func(index) { - f(self[index]) - } - ) - }; - - /// Applies `f` to each element of `array` in place, - /// retaining the original ordering of elements. - /// This modifies the original array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 0, 1, 2, 3]; - /// VarArray.mapInPlace(array, func x = x * 3); - /// assert VarArray.equal(array, [var 0, 3, 6, 9], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapInPlace(self : [var T], f : T -> T) { - var index = 0; - let size = self.size(); - while (index < size) { - self[index] := f(self[index]); - index += 1 - } - }; - - /// Creates a new mutable array by applying `predicate` to every element - /// in `array`, retaining the elements for which `predicate` returns true. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 4, 2, 6, 1, 5]; - /// let evenElements = VarArray.filter(array, func x = x % 2 == 0); - /// assert VarArray.equal(evenElements, [var 4, 2, 6], Nat.equal); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func filter(self : [var T], f : T -> Bool) : [var T] { - var count = 0; - let keep = Prim.Array_tabulate( - self.size(), - func i { - if (f(self[i])) { - count += 1; - true - } else { - false - } - } - ); - var nextKeep = 0; - tabulate( - count, - func _ { - while (not keep[nextKeep]) { - nextKeep += 1 - }; - nextKeep += 1; - self[nextKeep - 1] - } - ) - }; - - /// Creates a new mutable array by applying `f` to each element in `array`, - /// and keeping all non-null elements. The ordering is retained. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let array = [var 4, 2, 0, 1]; - /// let newArray = - /// VarArray.filterMap( // mapping from Nat to Text values - /// array, - /// func x = if (x == 0) { null } else { ?Nat.toText(100 / x) } // can't divide by 0, so return null - /// ); - /// assert VarArray.equal(newArray, [var "25", "50", "100"], Text.equal); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func filterMap(self : [var T], f : T -> ?R) : [var R] { - var count = 0; - let options = Prim.Array_tabulate( - self.size(), - func i { - let result = f(self[i]); - switch (result) { - case (?element) { - count += 1; - result - }; - case null { - null - } - } - } - ); - - var nextSome = 0; - tabulate( - count, - func _ { - while (Option.isNull(options[nextSome])) { - nextSome += 1 - }; - nextSome += 1; - switch (options[nextSome - 1]) { - case (?element) element; - case null { - Prim.trap "VarArray.filterMap(): malformed array" - } - } - } - ) - }; - - /// Creates a new mutable array by applying `f` to each element in `array`. - /// If any invocation of `f` produces an `#err`, returns an `#err`. Otherwise - /// returns an `#ok` containing the new array. - /// - /// ```motoko include=import - /// import Result "mo:core/Result"; - /// - /// let array = [var 4, 3, 2, 1, 0]; - /// // divide 100 by every element in the array - /// let result = VarArray.mapResult(array, func x { - /// if (x > 0) { - /// #ok(100 / x) - /// } else { - /// #err "Cannot divide by zero" - /// } - /// }); - /// assert Result.isErr(result); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - /// @deprecated M0235 - public func mapResult(self : [var T], f : T -> Result.Result) : Result.Result<[var R], E> { - let size = self.size(); - - var error : ?Result.Result<[var R], E> = null; - let results = tabulate( - size, - func i { - switch (f(self[i])) { - case (#ok element) { - ?element - }; - case (#err e) { - switch (error) { - case null { - // only take the first error - error := ?(#err e) - }; - case _ {} - }; - null - } - } - } - ); - - switch error { - case null { - // unpack the option - #ok( - map( - results, - func element { - switch element { - case (?element) { - element - }; - case null { - Prim.trap "VarArray.mapResults(): malformed array" - } - } - } - ) - ) - }; - case (?error) { - error - } - } - }; - - /// Creates a new array by applying `f` to each element in `array` and its index. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 10, 10, 10, 10]; - /// let newArray = VarArray.mapEntries(array, func (x, i) = i * x); - /// assert VarArray.equal(newArray, [var 0, 10, 20, 30], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapEntries(self : [var T], f : (T, Nat) -> R) : [var R] { - tabulate(self.size(), func i = f(self[i], i)) - }; - - /// Creates a new mutable array by applying `k` to each element in `array`, - /// and concatenating the resulting arrays in order. - /// - /// ```motoko include=import - /// import Int "mo:core/Int" - /// - /// let array = [var 1, 2, 3, 4]; - /// let newArray = VarArray.flatMap(array, func x = [x, -x].vals()); - /// assert VarArray.equal(newArray, [var 1, -1, 2, -2, 3, -3, 4, -4], Int.equal); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `k` runs in O(1) time and space. - public func flatMap(self : [var T], k : T -> Types.Iter) : [var R] { - var flatSize = 0; - let arrays = Prim.Array_tabulate<[var R]>( - self.size(), - func i { - let subArray = fromIter(k(self[i])); // TODO: optimize - flatSize += subArray.size(); - subArray - } - ); - - // could replace with a call to flatten, - // but it would require an extra pass (to compute `flatSize`) - var outer = 0; - var inner = 0; - tabulate( - flatSize, - func _ { - while (inner == arrays[outer].size()) { - inner := 0; - outer += 1 - }; - let element = arrays[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// ```motoko include=import - /// import {add} "mo:core/Nat"; - /// - /// let array = [var 4, 2, 0, 1]; - /// let sum = - /// VarArray.foldLeft( - /// array, - /// 0, // start the sum at 0 - /// func(sumSoFar, x) = sumSoFar + x // this entire function can be replaced with `add`! - /// ); - /// assert sum == 7; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(self : [var T], base : A, combine : (A, T) -> A) : A { - var acc = base; - for (element in self.vals()) { - acc := combine(acc, element) - }; - acc - }; - - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// ```motoko include=import - /// import {toText} "mo:core/Nat"; - /// - /// let array = [var 1, 9, 4, 8]; - /// let bookTitle = VarArray.foldRight(array, "", func(x, acc) = toText(x) # acc); - /// assert bookTitle == "1948"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldRight(self : [var T], base : A, combine : (T, A) -> A) : A { - var acc = base; - let size = self.size(); - var i = size; - while (i > 0) { - i -= 1; - acc := combine(self[i], acc) - }; - acc - }; - - /// Combines an iterator of mutable arrays into a single mutable array. - /// Retains the original ordering of the elements. - /// - /// Consider using `VarArray.flatten()` for better performance. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let arrays : [[var Nat]] = [[var 0, 1, 2], [var 2, 3], [var], [var 4]]; - /// let joinedArray = VarArray.join(arrays.vals()); - /// assert VarArray.equal(joinedArray, [var 0, 1, 2, 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(number of elements in array) - /// - /// Space: O(number of elements in array) - public func join(self : Types.Iter<[var T]>) : [var T] { - flatten(fromIter(self)) - }; - - /// Combines a mutable array of mutable arrays into a single mutable array. Retains the original - /// ordering of the elements. - /// - /// This has better performance compared to `VarArray.join()`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let arrays : [var [var Nat]] = [var [var 0, 1, 2], [var 2, 3], [var], [var 4]]; - /// let flatArray = VarArray.flatten(arrays); - /// assert VarArray.equal(flatArray, [var 0, 1, 2, 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(number of elements in array) - /// - /// Space: O(number of elements in array) - public func flatten(self : [var [var T]]) : [var T] { - var flatSize = 0; - for (subArray in self.vals()) { - flatSize += subArray.size() - }; - - var outer = 0; - var inner = 0; - tabulate( - flatSize, - func _ { - while (inner == self[outer].size()) { - inner := 0; - outer += 1 - }; - let element = self[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Create an array containing a single value. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = VarArray.singleton(2); - /// assert VarArray.equal(array, [var 2], Nat.equal); - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func singleton(element : T) : [var T] = [var element]; - - /// Returns the size of a mutable array. Equivalent to `array.size()`. - public func size(self : [var T]) : Nat = self.size(); - - /// Returns whether a mutable array is empty, i.e. contains zero elements. - public func isEmpty(self : [var T]) : Bool = self.size() == 0; - - /// Transforms an immutable array into a mutable array. - /// - /// ```motoko include=import - /// let array = [0, 1, 2]; - /// let varArray = VarArray.fromArray(array); - /// assert varArray.size() == 3; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// @deprecated M0235 - public func fromArray(array : [T]) : [var T] = Prim.Array_tabulateVar(array.size(), func i = array[i]); - - /// Converts an iterator to a mutable array. - public func fromIter(iter : Types.Iter) : [var T] { - var list : Types.Pure.List = null; - var size = 0; - label l loop { - switch (iter.next()) { - case (?element) { - list := ?(element, list); - size += 1 - }; - case null { break l } - } - }; - if (size == 0) { return [var] }; - let array = Prim.Array_init( - size, - switch list { - case (?(h, _)) h; - case null { - Prim.trap("VarArray.fromIter(): unreachable") - } - } - ); - var i = size; - while (i > 0) { - i -= 1; - switch list { - case (?(h, t)) { - array[i] := h; - list := t - }; - case null { - Prim.trap("VarArray.fromIter(): unreachable") - } - } - }; - array - }; - - /// Returns an iterator (`Iter`) over the indices of `array`. - /// An iterator provides a single method `next()`, which returns - /// indices in order, or `null` when out of index to iterate over. - /// - /// NOTE: You can also use `array.keys()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// let array = [var 10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.keys()) { - /// sum += element; - /// }; - /// assert sum == 3; // 0 + 1 + 2 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func keys(self : [var T]) : Types.Iter = self.keys(); - - /// Iterator provides a single method `next()`, which returns - /// elements in order, or `null` when out of elements to iterate over. - /// - /// Note: You can also use `array.values()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// let array = [var 10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.values()) { - /// sum += element; - /// }; - /// assert sum == 33; // 10 + 11 + 12 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func values(self : [var T]) : Types.Iter = self.vals(); - - /// Returns an iterator that provides pairs of (index, element) in order, or `null` - /// when out of elements to iterate over. - /// - /// ```motoko include=import - /// let array = [var 10, 11, 12]; - /// - /// var sum = 0; - /// for ((index, element) in VarArray.enumerate(array)) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func enumerate(self : [var T]) : Types.Iter<(Nat, T)> = object { - let size = self.size(); - var index = 0; - public func next() : ?(Nat, T) { - if (index >= size) { - return null - }; - let i = index; - index += 1; - ?(i, self[i]) - } - }; - - /// Returns true if all elements in `array` satisfy the predicate function. - /// - /// ```motoko include=import - /// let array = [var 1, 2, 3, 4]; - /// assert VarArray.all(array, func x = x > 0); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func all(self : [var T], predicate : T -> Bool) : Bool { - for (element in self.values()) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Returns true if any element in `array` satisfies the predicate function. - /// - /// ```motoko include=import - /// let array = [var 1, 2, 3, 4]; - /// assert VarArray.any(array, func x = x > 3); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func any(self : [var T], predicate : T -> Bool) : Bool { - for (element in self.values()) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Returns the index of the first `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.indexOf(array, Char.equal, 'c') == ?0; - /// assert VarArray.indexOf(array, Char.equal, 'f') == ?2; - /// assert VarArray.indexOf(array, Char.equal, 'g') == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func indexOf(self : [var T], equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = nextIndexOf(self, equal, element, 0); - - /// Returns the index of the next occurence of `element` in the `array` starting from the `from` index (inclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.nextIndexOf(array, Char.equal, 'c', 0) == ?0; - /// assert VarArray.nextIndexOf(array, Char.equal, 'f', 0) == ?2; - /// assert VarArray.nextIndexOf(array, Char.equal, 'f', 2) == ?2; - /// assert VarArray.nextIndexOf(array, Char.equal, 'f', 3) == ?3; - /// assert VarArray.nextIndexOf(array, Char.equal, 'f', 4) == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func nextIndexOf(self : [var T], equal : (implicit : (T, T) -> Bool), element : T, fromInclusive : Nat) : ?Nat { - var index = fromInclusive; - let size = self.size(); - while (index < size) { - if (equal(self[index], element)) { - return ?index - } else { - index += 1 - } - }; - null - }; - - /// Returns the index of the last `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.lastIndexOf(array, Char.equal, 'c') == ?0; - /// assert VarArray.lastIndexOf(array, Char.equal, 'f') == ?3; - /// assert VarArray.lastIndexOf(array, Char.equal, 'e') == ?5; - /// assert VarArray.lastIndexOf(array, Char.equal, 'g') == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func lastIndexOf(self : [var T], equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = prevIndexOf(self, equal, element, self.size()); - - /// Returns the index of the previous occurence of `element` in the `array` starting from the `from` index (exclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.prevIndexOf(array, Char.equal, 'c', array.size()) == ?0; - /// assert VarArray.prevIndexOf(array, Char.equal, 'e', array.size()) == ?5; - /// assert VarArray.prevIndexOf(array, Char.equal, 'e', 5) == ?4; - /// assert VarArray.prevIndexOf(array, Char.equal, 'e', 4) == null; - /// ``` - /// - /// Runtime: O(array.size()); - /// Space: O(1); - public func prevIndexOf(self : [var T], equal : (implicit : (T, T) -> Bool), element : T, fromExclusive : Nat) : ?Nat { - var i = fromExclusive; - while (i > 0) { - i -= 1; - if (equal(self[i], element)) { - return ?i - } - }; - null - }; - - /// Returns true if the `array` contains `element` using the provided `equal` function. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.contains(array, Char.equal, 'f'); - /// assert not VarArray.contains(array, Char.equal, 'g'); - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func contains(self : [var T], equal : (implicit : (T, T) -> Bool), element : T) : Bool { - for (item in self.vals()) { - if (equal(item, element)) { - return true - } - }; - false - }; - - /// Returns an iterator over a slice of `array` starting at `fromInclusive` up to (but not including) `toExclusive`. - /// - /// Negative indices are relative to the end of the array. For example, `-1` corresponds to the last element in the array. - /// - /// If the indices are out of bounds, they are clamped to the array bounds. - /// If the first index is greater than the second, the function returns an empty iterator. - /// - /// ```motoko include=import - /// let array = [var 1, 2, 3, 4, 5]; - /// let iter1 = VarArray.range(array, 3, array.size()); - /// assert iter1.next() == ?4; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// let iter2 = VarArray.range(array, 3, -1); - /// assert iter2.next() == ?4; - /// assert iter2.next() == null; - /// - /// let iter3 = VarArray.range(array, 0, 0); - /// assert iter3.next() == null; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func range(self : [var T], fromInclusive : Int, toExclusive : Int) : Types.Iter { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (values are non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - object { - var pos = start; - public func next() : ?T { - if (pos >= end) { - null - } else { - let elem = self[pos]; - pos += 1; - ?elem - } - } - } - }; - - /// Returns a new array containing elements from `array` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// let array = [var 1, 2, 3, 4, 5]; - /// - /// let slice1 = VarArray.sliceToArray(array, 1, 4); - /// assert slice1 == [2, 3, 4]; - /// - /// let slice2 = VarArray.sliceToArray(array, 1, -1); - /// assert slice2 == [2, 3, 4]; - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToArray(self : [var T], fromInclusive : Int, toExclusive : Int) : [T] { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - if (start >= end) { - return [] - }; - Prim.Array_tabulate(end - start, func i = self[start + i]) - }; - - /// Returns a new mutable array containing elements from `array` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 1, 2, 3, 4, 5]; - /// - /// let slice1 = VarArray.sliceToVarArray(array, 1, 4); - /// assert VarArray.equal(slice1, [var 2, 3, 4], Nat.equal); - /// - /// let slice2 = VarArray.sliceToVarArray(array, 1, -1); - /// assert VarArray.equal(slice2, [var 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToVarArray(self : [var T], fromInclusive : Int, toExclusive : Int) : [var T] { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - if (start >= end) { - return [var] - }; - Prim.Array_tabulateVar(end - start, func i = self[start + i]) - }; - - /// Transforms a mutable array into an immutable array. - /// - /// ```motoko include=import - /// let varArray = [var 0, 1, 2]; - /// varArray[2] := 3; - /// let array = VarArray.toArray(varArray); - /// assert array == [0, 1, 3]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func toArray(self : [var T]) : [T] = Prim.Array_tabulate(self.size(), func i = self[i]); - - /// Converts the mutable array to its textual representation using `f` to convert each element to `Text`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 1, 2, 3]; - /// assert VarArray.toText(array, Nat.toText) == "[var 1, 2, 3]"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func toText(self : [var T], f : (implicit : (toText : T -> Text))) : Text { - let size = self.size(); - if (size == 0) { return "[var]" }; - var text = "[var "; - var i = 0; - while (i < size) { - if (i != 0) { - text #= ", " - }; - text #= f(self[i]); - i += 1 - }; - text #= "]"; - text - }; - - /// Compares two mutable arrays using the provided comparison function for elements. - /// Returns #less, #equal, or #greater if `array1` is less than, equal to, - /// or greater than `array2` respectively. - /// - /// If arrays have different sizes but all elements up to the shorter length are equal, - /// the shorter array is considered #less than the longer array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// let array1 = [var 1, 2, 3]; - /// let array2 = [var 1, 2, 4]; - /// assert VarArray.compare(array1, array2, Nat.compare) == #less; - /// - /// let array3 = [var 1, 2]; - /// let array4 = [var 1, 2, 3]; - /// assert VarArray.compare(array3, array4, Nat.compare) == #less; - /// ``` - /// - /// Runtime: O(min(size1, size2)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func compare(self : [var T], other : [var T], compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let size1 = self.size(); - let size2 = other.size(); - var i = 0; - let minSize = if (size1 < size2) { size1 } else { size2 }; - while (i < minSize) { - switch (compare(self[i], other[i])) { - case (#less) { return #less }; - case (#greater) { return #greater }; - case (#equal) { i += 1 } - } - }; - if (size1 < size2) { #less } else if (size1 > size2) { #greater } else { - #equal - } - }; - - /// Performs binary search on a sorted mutable array to find the index of the `element`. - /// Returns `#found(index)` if the element is found, or `#insertionIndex(index)` with the index - /// - /// If there are multiple equal elements, no guarantee is made about which index is returned. - /// The array must be sorted in ascending order according to the `compare` function. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let sorted = [var 1, 3, 5, 7, 9, 11]; - /// assert VarArray.binarySearch(sorted, Nat.compare, 5) == #found(2); - /// assert VarArray.binarySearch(sorted, Nat.compare, 6) == #insertionIndex(3); - /// ``` - /// - /// Runtime: O(log(size)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func binarySearch(self : [var T], compare : (implicit : (T, T) -> Order.Order), element : T) : { - #found : Nat; - #insertionIndex : Nat - } { - var left = 0; - var right = self.size(); - while (left < right) { - let mid = (left + right) / 2; - switch (compare(self[mid], element)) { - case (#less) left := mid + 1; - case (#greater) right := mid; - case (#equal) return #found mid - } - }; - #insertionIndex left - }; - - /// Checks whether the mutable `array` is sorted according to the `compare` function. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 1, 2, 3]; - /// assert VarArray.isSorted(array, Nat.compare); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func isSorted(self : [var T], compare : (implicit : (T, T) -> Order.Order)) : Bool { - let size = self.size(); - if (size <= 1) return true; - var i = 1; - while (i < size) { - switch (compare(self[i - 1], self[i])) { - case (#greater) return false; - case _ { i += 1 } - } - }; - true - } - -} diff --git a/.mops/core@2.4.0/src/WeakReference.mo b/.mops/core@2.4.0/src/WeakReference.mo deleted file mode 100644 index a7c4a69..0000000 --- a/.mops/core@2.4.0/src/WeakReference.mo +++ /dev/null @@ -1,59 +0,0 @@ -/// Module that implements a weak reference to an object. -/// -/// ATTENTION: This functionality does not work with classical persistence (`--legacy-persistence` moc flag). -/// -/// Usage example: -/// Import from the core package to use this module. -/// ```motoko name=import -/// import WeakReference "mo:core/WeakReference"; -/// ``` - -import Prim "mo:⛔" - -module { - public type WeakReference = { - ref : weak T - }; - - /// Allocate a new weak reference to the given object. - /// - /// The `obj` parameter is the object to allocate a weak reference for. - /// Returns a new weak reference pointingto the given object. - /// ```motoko include=import - /// let obj = { x = 1 }; - /// let weakRef = WeakReference.allocate(obj); - /// ``` - public func allocate(obj : T) : WeakReference { - return { ref = Prim.allocWeakRef(obj) } - }; - - /// Get the value that the weak reference is pointing to. - /// - /// The `self` parameter is the weak reference pointing to the value the function returns. - /// The function returns the value that the weak reference is pointing to, - /// or `null` if the value has been collected by the garbage collector. - /// ```motoko include=import - /// let obj = { x = 1 }; - /// let weakRef = WeakReference.allocate(obj); - /// let value = weakRef.get(); - /// ``` - public func get(self : WeakReference) : ?T { - return Prim.weakGet(self.ref) - }; - - /// Check if the weak reference is still alive. - /// - /// The `self` parameter is the weak reference to check whether it is still alive. - /// Returns `true` if the weak reference is still alive, `false` otherwise. - /// False means that the value has been collected by the garbage collector. - /// ```motoko include=import - /// let obj = { x = 1 }; - /// let weakRef = WeakReference.allocate(obj); - /// let isLive = weakRef.isLive(); - /// assert isLive == true; - /// ``` - public func isLive(self : WeakReference) : Bool { - return Prim.isLive(self.ref) - }; - -} diff --git a/.mops/core@2.4.0/src/internal/BTreeHelper.mo b/.mops/core@2.4.0/src/internal/BTreeHelper.mo deleted file mode 100644 index 888087d..0000000 --- a/.mops/core@2.4.0/src/internal/BTreeHelper.mo +++ /dev/null @@ -1,412 +0,0 @@ -// Implementation is courtesy of Byron Becker. -// Source: https://github.com/canscale/StableHeapBTreeMap -// Copyright (c) 2022 Byron Becker. -// Distributed under Apache 2.0 license. -// With adjustments by the Motoko team. - -import VarArray "../VarArray"; -import Runtime "../Runtime"; - -module { - /// Inserts an element into a mutable array at a specific index, shifting all other elements over - /// - /// Parameters: - /// - /// array - the array being inserted into - /// insertElement - the element being inserted - /// insertIndex - the index at which the element will be inserted - /// currentLastElementIndex - the index of last **non-null** element in the array (used to start shifting elements over) - /// - /// Note: This assumes that there are nulls at the end of the array and that the array is not full. - /// If the array is already full, this function will overflow the array size when attempting to - /// insert and will cause the cansiter to trap - public func insertAtPosition(array : [var ?T], insertElement : ?T, insertIndex : Nat, currentLastElementIndex : Nat) { - // if inserting at the end of the array, don't need to do any shifting and can just insert and return - if (insertIndex == currentLastElementIndex + 1) { - array[insertIndex] := insertElement; - return - }; - - // otherwise, need to shift all of the elements at the end of the array over one by one until - // the insert index is hit. - var j = currentLastElementIndex; - label l loop { - array[j + 1] := array[j]; - if (j == insertIndex) { - array[j] := insertElement; - break l - }; - - j -= 1 - } - }; - - /// Splits the array into two halves as if the insert has occured, omitting the middle element and returning it so that it can - /// be promoted to the parent internal node. This is used when inserting an element into an array of elements that - /// is already full. - /// - /// Note: Use only when inserting an element into a FULL array & promoting the resulting midpoint element. - /// This is NOT the same as just splitting this array! - /// - /// Parameters: - /// - /// array - the array being split - /// insertElement - the element being inserted - /// insertIndex - the position/index that the insertElement should be inserted - public func insertOneAtIndexAndSplitArray(array : [var ?T], insertElement : T, insertIndex : Nat) : ([var ?T], T, [var ?T]) { - // split at the BTree order / 2 - let splitIndex = (array.size() + 1) / 2; - // this function assumes the the splitIndex is in the middle of the kvs array - trap otherwise - if (splitIndex > array.size()) { assert false }; - - let leftSplit = if (insertIndex < splitIndex) { - VarArray.tabulate( - array.size(), - func(i) { - // if below the split index - if (i < splitIndex) { - // if below the insert index, copy over - if (i < insertIndex) { array[i] } - // if less than the insert index, copy over the previous element (since the inserted element has taken up 1 extra slot) - else if (i > insertIndex) { array[i - 1] } - // if equal to the insert index add the element to be inserted to the left split - else { ?insertElement } - } else { null } - } - ) - } - // index >= splitIndex - else { - VarArray.tabulate( - array.size(), - func(i) { - // right biased splitting - if (i < splitIndex) { array[i] } else { null } - } - ) - }; - - let (rightSplit, middleElement) : ([var ?T], ?T) = - // if insert > split index, inserted element will be inserted into the right split - if (insertIndex > splitIndex) { - let right = VarArray.tabulate( - array.size(), - func(i) { - let adjIndex = i + splitIndex + 1; // + 1 accounts for the fact that the split element was part of the original array - if (adjIndex <= array.size()) { - if (adjIndex < insertIndex) { array[adjIndex] } else if (adjIndex > insertIndex) { - array[adjIndex - 1] - } else { ?insertElement } - } else { null } - } - ); - (right, array[splitIndex]) - } - // if inserted element was placed in the left split - else if (insertIndex < splitIndex) { - let right = VarArray.tabulate( - array.size(), - func(i) { - let adjIndex = i + splitIndex; - if (adjIndex < array.size()) { array[adjIndex] } else { null } - } - ); - (right, array[splitIndex - 1]) - } - // insertIndex == splitIndex - else { - let right = VarArray.tabulate( - array.size(), - func(i) { - let adjIndex = i + splitIndex; - if (adjIndex < array.size()) { array[adjIndex] } else { null } - } - ); - (right, ?insertElement) - }; - - switch (middleElement) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: insertOneAtIndexAndSplitArray, middle element of a BTree node should never be null") - }; - case (?el) { (leftSplit, el, rightSplit) } - } - }; - - /// Context of use: This function is used after inserting a child node into the full child of an internal node that is also full. - /// From the insertion, the full child is rebalanced and split, and then since the internal node is full, when replacing the two - /// halves of that rebalanced child into the internal node's children this causes a second split. This function takes in the - /// internal node's children, and the "rebalanced" split child nodes, as well as the index at which the "rebalanced" left and right - /// child will be inserted and replaces the original child with those two halves - /// - /// Note: Use when inserting two successive elements into a FULL array and splitting that array. - /// This is NOT the same as just splitting this array! - /// - /// Assumptions: this function also assumes that the children array is full (no nulls) - /// - /// Parameters: - /// - /// children - the internal node's children array being split - /// rebalancedChildIndex - the index used to mark where the rebalanced left and right children will be inserted - /// leftChildInsert - the rebalanced left child being inserted - /// rightChildInsert - the rebalanced right child being inserted - public func splitArrayAndInsertTwo(children : [var ?T], rebalancedChildIndex : Nat, leftChildInsert : T, rightChildInsert : T) : ([var ?T], [var ?T]) { - let splitIndex = children.size() / 2; - - let leftRebalancedChildren = VarArray.tabulate( - children.size(), - func(i) { - // only insert elements up to the split index and fill the rest of the children with nulls - if (i <= splitIndex) { - if (i < rebalancedChildIndex) { children[i] } - // insert the left and right rebalanced child halves if the rebalancedChildIndex comes before the splitIndex - else if (i == rebalancedChildIndex) { - ?leftChildInsert - } else if (i == rebalancedChildIndex + 1) { ?rightChildInsert } else { - children[i - 1] - } // i > rebalancedChildIndex - } else { null } - } - ); - - let rightRebalanceChildren : [var ?T] = - // Case 1: if both left and right rebalanced halves were inserted into the left child can just go from the split index onwards - if (rebalancedChildIndex + 1 <= splitIndex) { - VarArray.tabulate( - children.size(), - func(i) { - let adjIndex = i + splitIndex; - if (adjIndex < children.size()) { children[adjIndex] } else { null } - } - ) - } - // Case 2: if both left and right rebalanced halves will be inserted into the right child - else if (rebalancedChildIndex > splitIndex) { - var rebalanceOffset = 0; - VarArray.tabulate( - children.size(), - func(i) { - let adjIndex = i + splitIndex + 1; - if (adjIndex == rebalancedChildIndex) { ?leftChildInsert } else if (adjIndex == rebalancedChildIndex + 1) { - rebalanceOffset := 1; // after inserting both rebalanced children, any elements coming after are from the previous index - ?rightChildInsert - } else if (adjIndex <= children.size()) { - children[adjIndex - rebalanceOffset] - } else { null } - } - ) - } - // Case 3: if left rebalanced half was in left child, and right rebalanced half will be in right child - // rebalancedChildIndex == splitIndex - else { - VarArray.tabulate( - children.size(), - func(i) { - // first element is the right rebalanced half - if (i == 0) { ?rightChildInsert } else { - let adjIndex = i + splitIndex; - if (adjIndex < children.size()) { children[adjIndex] } else { - null - } - } - } - ) - }; - - (leftRebalancedChildren, rightRebalanceChildren) - }; - - /// Specific to the BTree delete implementation (assumes node ordering such that nulls come at the end of the array) - /// - /// Assumptions: - /// * All nulls come at the end of the array - /// * Assumes the delete index provided is correct and non null - will trap otherwise - /// * deleteIndex < array.size() - /// - /// Deletes an element from the the array, and then shifts all non-null elements coming after that deleted element by 1 - /// to the left. Returns the element that was deleted. - public func deleteAndShift(array : [var ?T], deleteIndex : Nat) : T { - var deleted : T = switch (array[deleteIndex]) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: deleteAndShift, an invalid/incorrect delete index was passed") - }; - case (?el) { el } - }; - - array[deleteIndex] := null; - - var i = deleteIndex + 1; - label l loop { - if (i >= array.size()) { break l }; - - switch (array[i]) { - case null { break l }; - case (?_) { - array[i - 1] := array[i] - } - }; - - i += 1 - }; - - array[i - 1] := null; - - deleted - }; - - // replaces two successive elements in the array with a single element and shifts all other elements to the left by 1 - public func replaceTwoWithElementAndShift(array : [var ?T], element : T, replaceIndex : Nat) { - array[replaceIndex] := ?element; - - var i = replaceIndex + 1; - let endShiftIndex : Nat = array.size() - 1; - while (i < endShiftIndex) { - switch (array[i]) { - case (?_) { array[i] := array[i + 1] }; - case null { return } - }; - - i += 1 - }; - - array[endShiftIndex] := null - }; - - /// BTree specific implementation - /// - /// In a single iteration insert at one position of the array while deleting at another position of the array, shifting all - /// elements as appropriate - /// - /// This is used when borrowing an element from an inorder predecessor/successor through the parent node - public func insertAtPostionAndDeleteAtPosition(array : [var ?T], insertElement : ?T, insertIndex : Nat, deleteIndex : Nat) : T { - var deleted : T = switch (array[deleteIndex]) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: insertAtPositionAndDeleteAtPosition, and incorrect delete index was passed") - }; // indicated an incorrect delete index was passed - trap - case (?el) { el } - }; - - // Example of this case: - // - // Insert Delete - // V V - //[var ?10, ?20, ?30, ?40, ?50] - if (insertIndex < deleteIndex) { - var i = deleteIndex; - while (i > insertIndex) { - array[i] := array[i - 1]; - i -= 1 - }; - - array[insertIndex] := insertElement - } - // Example of this case: - // - // Delete Insert - // V V - //[var ?10, ?20, ?30, ?40, ?50] - else if (insertIndex > deleteIndex) { - array[deleteIndex] := null; - var i = deleteIndex + 1; - label l loop { - if (i >= array.size()) { assert false; break l }; // TODO: remove? this should not happen since the insertIndex should get hit first? - - if (i == insertIndex) { - array[i - 1] := array[i]; - array[i] := insertElement; - break l - } else { - array[i - 1] := array[i] - }; - - i += 1 - }; - - } - // insertIndex == deleteIndex, can just do a swap - else { array[deleteIndex] := insertElement }; - - deleted - }; - - // which child the deletionIndex is referring to - public type DeletionSide = { #left; #right }; - - // merges a middle (parent) element with the left and right child arrays while deleting the element from the correct child by the deleteIndex passed - public func mergeParentWithChildrenAndDelete( - parentElement : ?T, - childCount : Nat, - leftChild : [var ?T], - rightChild : [var ?T], - deleteIndex : Nat, - deletionSide : DeletionSide - ) : ([var ?T], T) { - let mergedArray = VarArray.repeat(null, leftChild.size()); - var i = 0; - switch (deletionSide) { - case (#left) { - // BTree implementation expects the deleted element to exist - if null, traps - let deletedElement = switch (leftChild[deleteIndex]) { - case (?el) { el }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: mergeParentWithChildrenAndDelete, an invalid delete index was passed") - } - }; - - // copy over left child until deleted element is hit, then copy all elements after the deleted element - while (i < childCount) { - if (i < deleteIndex) { - mergedArray[i] := leftChild[i] - } else { - mergedArray[i] := leftChild[i + 1] - }; - i += 1 - }; - - // insert parent kv in the middle - mergedArray[childCount - 1] := parentElement; - - // copy over the rest of the right child elements - while (i < childCount * 2) { - mergedArray[i] := rightChild[i - childCount]; - i += 1 - }; - - (mergedArray, deletedElement) - }; - case (#right) { - // BTree implementation expects the deleted element to exist - if null, traps - let deletedElement = switch (rightChild[deleteIndex]) { - case (?el) { el }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: mergeParentWithChildrenAndDelete: element at deleted index must exist") - } - }; - // since deletion side is #right, can safely copy over all elements from the left child - while (i < childCount) { - mergedArray[i] := leftChild[i]; - i += 1 - }; - - // insert parent kv in the middle - mergedArray[childCount] := parentElement; - i += 1; - - var j = 0; - // copy over right child until deleted element is hit, then copy elements after the deleted element - while (i < childCount * 2) { - if (j < deleteIndex) { - mergedArray[i] := rightChild[j] - } else { - mergedArray[i] := rightChild[j + 1] - }; - i += 1; - j += 1 - }; - - (mergedArray, deletedElement) - } - } - }; - -} diff --git a/.mops/core@2.4.0/src/internal/PRNG.mo b/.mops/core@2.4.0/src/internal/PRNG.mo deleted file mode 100644 index 8a59861..0000000 --- a/.mops/core@2.4.0/src/internal/PRNG.mo +++ /dev/null @@ -1,76 +0,0 @@ -/// Collection of pseudo-random number generators -/// -/// The algorithms deliver deterministic statistical randomness, -/// not cryptographic randomness. -/// -/// Algorithm 1: 128-bit Seiran PRNG -/// See: https://github.com/andanteyk/prng-seiran -/// -/// Algorithm 2: SFC64 and SFC32 (Chris Doty-Humphrey’s Small Fast Chaotic PRNG) -/// See: https://numpy.org/doc/stable/reference/random/bit_generators/sfc64.html -/// -/// Copyright: 2023 MR Research AG -/// Main author: react0r-com -/// Contributors: Timo Hanke (timohanke) -import Nat "../Nat"; - -module { - /// Constructs an SFC 64-bit generator. - /// The recommended constructor arguments are: 24, 11, 3. - /// - /// Example: - /// ```motoko - /// import PRNG "mo:core/internal/PRNG"; - /// - /// let rng = PRNG.SFC64(24, 11, 3); - /// ``` - /// For convenience, the function `SFC64a()` returns a generator constructed - /// with the recommended parameter set (24, 11, 3). - public class SFC64(p : Nat64, q : Nat64, r : Nat64) { - // state - var a : Nat64 = 0; - var b : Nat64 = 0; - var c : Nat64 = 0; - var d : Nat64 = 0; - - /// Initializes the PRNG state with a particular seed - /// - /// Example: - /// ```motoko - public func init(seed : Nat64) = init3(seed, seed, seed); - - /// Initializes the PRNG state with a hardcoded seed. - /// No argument is required. - /// - /// Example: - public func initPre() = init(0xcafef00dbeef5eed); - - /// Initializes the PRNG state with three state variables - /// - /// Example: - public func init3(seed1 : Nat64, seed2 : Nat64, seed3 : Nat64) { - a := seed1; - b := seed2; - c := seed3; - d := 1; - - for (_ in Nat.range(0, 11)) ignore next() - }; - - /// Returns one output and advances the PRNG's state - /// - /// Example: - public func next() : Nat64 { - let tmp = a +% b +% d; - a := b ^ (b >> q); - b := c +% (c << r); - c := (c <<> p) +% tmp; - d +%= 1; - tmp - } - }; - - /// SFC64a is the same as numpy. - /// See: [sfc64_next()](https:///github.com/numpy/numpy/blob/b6d372c25fab5033b828dd9de551eb0b7fa55800/numpy/random/src/sfc64/sfc64.h#L28) - public func sfc64a() : SFC64 { SFC64(24, 11, 3) } -} diff --git a/.mops/core@2.4.0/src/internal/SortHelper.mo b/.mops/core@2.4.0/src/internal/SortHelper.mo deleted file mode 100644 index 2222e23..0000000 --- a/.mops/core@2.4.0/src/internal/SortHelper.mo +++ /dev/null @@ -1,1270 +0,0 @@ -import Runtime "../Runtime"; -import Order "../Order"; -import Prim "mo:⛔"; - -module { - let nat = Prim.nat32ToNat; - - // Must have: len <= 8 - // Use dest = buffer when sorting in place - public func insertionSortSmall(buffer : [var T], dest : [var T], compare : (T, T) -> Order.Order, newFrom : Nat32, len : Nat32) { - debug assert len > 0; - switch (len) { - case (1) { - let index0 = nat(newFrom); - dest[index0] := buffer[index0] - }; - case (2) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let t0 = buffer[index0]; - let t1 = buffer[index1]; - switch (compare(t1, t0)) { - case (#less) { - dest[index0] := t1; - dest[index1] := t0 - }; - case (_) { - dest[index0] := t0; - dest[index1] := t1 - } - } - }; - case (3) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - let t2 = buffer[index2]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - - switch (compare(t2, t1)) { - case (#less) { - switch (compare(t2, t0)) { - case (#less) { - dest[index0] := t2; - dest[index1] := t0; - dest[index2] := t1 - }; - case (_) { - dest[index0] := t0; - dest[index1] := t2; - dest[index2] := t1 - } - } - }; - case (_) { - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2 - } - } - }; - case (4) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - - switch (compare(t3, t2)) { - case (#less) { - tv := t3; - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3 - }; - case (5) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - let index4 = nat(newFrom +% 4); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - var t4 = buffer[index4]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3; - dest[index4] := t4 - }; - case (6) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - let index4 = nat(newFrom +% 4); - let index5 = nat(newFrom +% 5); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - var t4 = buffer[index4]; - var t5 = buffer[index5]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3; - dest[index4] := t4; - dest[index5] := t5 - }; - case (7) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - let index4 = nat(newFrom +% 4); - let index5 = nat(newFrom +% 5); - let index6 = nat(newFrom +% 6); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - var t4 = buffer[index4]; - var t5 = buffer[index5]; - var t6 = buffer[index6]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - tv := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3; - dest[index4] := t4; - dest[index5] := t5; - dest[index6] := t6 - }; - case (8) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - let index4 = nat(newFrom +% 4); - let index5 = nat(newFrom +% 5); - let index6 = nat(newFrom +% 6); - let index7 = nat(newFrom +% 7); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - var t4 = buffer[index4]; - var t5 = buffer[index5]; - var t6 = buffer[index6]; - var t7 = buffer[index7]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - tv := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) {} - }; - tv := t7; - switch (compare(tv, t6)) { - case (#less) { - t7 := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) { t6 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3; - dest[index4] := t4; - dest[index5] := t5; - dest[index6] := t6; - dest[index7] := t7 - }; - case (_) Runtime.trap("insertionSortSmall for len > 8 is not implemented.") - } - }; - - // sort from buffer to dest array at the given offset - public func insertionSortSmallMove(buffer : [var T], dest : [var T], compare : (T, T) -> Order.Order, newFrom : Nat32, len : Nat32, offset : Nat32) { - debug assert len > 0; - switch (len) { - case (1) { - dest[nat(offset)] := buffer[nat(newFrom)] - }; - case (2) { - let t0 = buffer[nat(newFrom)]; - let t1 = buffer[nat(newFrom +% 1)]; - switch (compare(t1, t0)) { - case (#less) { - dest[nat(offset)] := t1; - dest[nat(offset +% 1)] := t0 - }; - case (_) { - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1 - } - } - }; - case (3) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - let t2 = buffer[nat(newFrom +% 2)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - - switch (compare(t2, t1)) { - case (#less) { - switch (compare(t2, t0)) { - case (#less) { - dest[nat(offset)] := t2; - dest[nat(offset +% 1)] := t0; - dest[nat(offset +% 2)] := t1 - }; - case (_) { - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t2; - dest[nat(offset +% 2)] := t1 - } - } - }; - case (_) { - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2 - } - } - }; - case (4) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - - switch (compare(t3, t2)) { - case (#less) { - tv := t3; - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3 - }; - case (5) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - var t4 = buffer[nat(newFrom +% 4)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3; - dest[nat(offset +% 4)] := t4 - }; - case (6) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - var t4 = buffer[nat(newFrom +% 4)]; - var t5 = buffer[nat(newFrom +% 5)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3; - dest[nat(offset +% 4)] := t4; - dest[nat(offset +% 5)] := t5 - }; - case (7) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - var t4 = buffer[nat(newFrom +% 4)]; - var t5 = buffer[nat(newFrom +% 5)]; - var t6 = buffer[nat(newFrom +% 6)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - tv := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3; - dest[nat(offset +% 4)] := t4; - dest[nat(offset +% 5)] := t5; - dest[nat(offset +% 6)] := t6 - }; - case (8) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - var t4 = buffer[nat(newFrom +% 4)]; - var t5 = buffer[nat(newFrom +% 5)]; - var t6 = buffer[nat(newFrom +% 6)]; - var t7 = buffer[nat(newFrom +% 7)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - tv := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) {} - }; - tv := t7; - switch (compare(tv, t6)) { - case (#less) { - t7 := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) { t6 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3; - dest[nat(offset +% 4)] := t4; - dest[nat(offset +% 5)] := t5; - dest[nat(offset +% 6)] := t6; - dest[nat(offset +% 7)] := t7 - }; - case (_) Runtime.trap("insertionSortSmall for len > 8 is not implemented.") - } - } -} diff --git a/.mops/core@2.4.0/src/pure/List.mo b/.mops/core@2.4.0/src/pure/List.mo deleted file mode 100644 index c0d36f1..0000000 --- a/.mops/core@2.4.0/src/pure/List.mo +++ /dev/null @@ -1,1114 +0,0 @@ -/// Purely-functional, singly-linked list data structure. -/// This module provides immutable lists with efficient prepend and traversal operations. -/// -/// A list of type `List` is either `null` or an optional pair of a value of type `T` and a tail, itself of type `List`. -/// -/// To use this library, import it using: -/// -/// ```motoko name=import -/// import List "mo:core/pure/List"; -/// ``` - -import { Array_tabulate } "mo:⛔"; -import Array "../Array"; -import Iter "../Iter"; -import Order "../Order"; -import Result "../Result"; -import { trap } "../Runtime"; -import Types "../Types"; -import Runtime "../Runtime"; - -module { - - /// @deprecated M0235 - public type List = Types.Pure.List; - - /// Create an empty list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// assert List.empty() == null; - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func empty() : List = null; - - /// Check whether a list is empty and return true if the list is empty. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// assert List.isEmpty(null); - /// assert not List.isEmpty(?(1, null)); - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func isEmpty(self : List) : Bool = switch self { - case null true; - case _ false - }; - - /// Return the length of the list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, null)); - /// assert List.size(list) == 2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func size(self : List) : Nat = ( - func go(n : Nat, list : List) : Nat = switch list { - case (?(_, t)) go(n + 1, t); - case null n - } - )(0, self); - - /// Check whether the list contains a given value. Uses the provided equality function to compare values. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.contains(list, Nat.equal, 2); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func contains(self : List, equal : (implicit : (T, T) -> Bool), item : T) : Bool = switch self { - case (?(h, t)) equal(h, item) or contains(t, equal, item); - case _ false - }; - - /// Access any item in a list, zero-based. - /// - /// NOTE: Indexing into a list is a linear operation, and usually an - /// indication that a list might not be the best data structure - /// to use. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, null)); - /// assert List.get(list, 1) == ?1; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func get(self : List, n : Nat) : ?T = switch self { - case (?(h, t)) if (n == 0) ?h else get(t, n - 1 : Nat); - case null null - }; - - /// Add `item` to the head of `list`, and return the new list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// assert List.pushFront(null, 0) == ?(0, null); - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func pushFront(self : List, item : T) : List = ?(item, self); - - /// Return the last element of the list, if present. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, null)); - /// assert List.last(list) == ?1; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func last(self : List) : ?T = switch self { - case (?(h, null)) ?h; - case null null; - case (?(_, t)) last t - }; - - /// Remove the head of the list, returning the optioned head and the tail of the list in a pair. - /// Returns `(null, null)` if the list is empty. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, null)); - /// assert List.popFront(list) == (?0, ?(1, null)); - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func popFront(self : List) : (?T, List) = switch self { - case null (null, null); - case (?(h, t)) (?h, t) - }; - - /// Reverses the list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.reverse(list) == ?(2, ?(1, ?(0, null))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func reverse(self : List) : List = ( - func go(acc : List, list : List) : List = switch list { - case (?(h, t)) go(?(h, acc), t); - case null acc - } - )(null, self); - - /// Call the given function for its side effect, with each list element in turn. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// var sum = 0; - /// List.forEach(list, func n = sum += n); - /// assert sum == 3; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEach(self : List, f : T -> ()) = switch self { - case (?(h, t)) { f h; forEach(t, f) }; - case null () - }; - - /// Call the given function `f` on each list element and collect the results - /// in a new list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.map(list, Nat.toText) == ?("0", ?("1", ?("2", null))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func map(self : List, f : T1 -> T2) : List = ( - func go(list : List, f : T1 -> T2, acc : List) : List = switch list { - case (?(h, t)) go(t, f, ?(f h, acc)); - case null reverse acc - } - )(self, f, null); - - /// Create a new list with only those elements of the original list for which - /// the given function (often called the _predicate_) returns true. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.filter(list, func n = n != 1) == ?(0, ?(2, null)); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func filter(self : List, f : T -> Bool) : List = ( - func go(list : List, f : T -> Bool, acc : List) : List = switch list { - case (?(h, t)) if (f h) go(t, f, ?(h, acc)) else go(t, f, acc); - case null reverse acc - } - )(self, f, null); - - /// Call the given function on each list element, and collect the non-null results - /// in a new list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.filterMap( - /// list, - /// func n = if (n > 1) ?(n * 2) else null - /// ) == ?(4, ?(6, null)); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func filterMap(self : List, f : T -> ?R) : List = ( - func go(list : List, f : T -> ?R, acc : List) : List = switch list { - case (?(h, t)) switch (f h) { - case null go(t, f, acc); - case (?r) go(t, f, ?(r, acc)) - }; - case null reverse acc - } - )(self, f, null); - - /// Maps a `Result`-returning function `f` over a `List` and returns either - /// the first error or a list of successful values. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.mapResult( - /// list, - /// func n = if (n > 0) #ok(n * 2) else #err "Some element is zero" - /// ) == #ok(?(2, ?(4, ?(6, null)))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapResult(self : List, f : T -> Result.Result) : Result.Result, E> = ( - func rev(acc : List, list : List, f : T -> Result.Result) : Result.Result, E> = switch list { - case (?(h, t)) switch (f h) { - case (#ok fh) rev(?(fh, acc), t, f); - case (#err e) #err e - }; - case null #ok(reverse acc) - } - )(null, self, f); - - /// Create two new lists from the results of a given function (`f`). - /// The first list only includes the elements for which the given - /// function `f` returns true and the second list only includes - /// the elements for which the function returns false. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.partition(list, func n = n != 1) == (?(0, ?(2, null)), ?(1, null)); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func partition(self : List, f : T -> Bool) : (List, List) = ( - func go(list : List, f : T -> Bool, acc1 : List, acc2 : List) : (List, List) = switch list { - case (?(h, t)) if (f h) go(t, f, ?(h, acc1), acc2) else go(t, f, acc1, ?(h, acc2)); - case null (reverse acc1, reverse acc2) - } - )(self, f, null, null); - - /// Append the elements from one list to another list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list1 = ?(0, ?(1, ?(2, null))); - /// let list2 = ?(3, ?(4, ?(5, null))); - /// assert List.concat(list1, list2) == ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))); - /// } - /// ``` - /// - /// Runtime: O(size(l)) - /// - /// Space: O(size(l)) - public func concat(self : List, other : List) : List = revAppend(reverse self, other); - - /// Flatten, or repatedly concatenate, an iterator of lists as a list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let lists = [ ?(0, ?(1, ?(2, null))), - /// ?(3, ?(4, ?(5, null))) ]; - /// assert List.join(lists |> Iter.fromArray(_)) == ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))); - /// } - /// ``` - /// - /// Runtime: O(size*size) - /// - /// Space: O(size*size) - public func join(iter : Iter.Iter>) : List { - var acc : List = null; - for (list in iter) { - acc := revAppend(list, acc) - }; - reverse acc - }; - - /// Flatten, or repatedly concatenate, a list of lists as a list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let lists = ?(?(0, ?(1, ?(2, null))), - /// ?(?(3, ?(4, ?(5, null))), - /// null)); - /// assert List.flatten(lists) == ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))); - /// } - /// ``` - /// - /// Runtime: O(size*size) - /// - /// Space: O(size*size) - public func flatten(self : List>) : List = ( - func go(lists : List>, acc : List) : List = switch lists { - case (?(list, t)) go(t, revAppend(list, acc)); - case null reverse acc - } - )(self, null); - - /// Returns the first `n` elements of the given list. - /// If the given list has fewer than `n` elements, this function returns - /// a copy of the full input list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.take(list, 2) == ?(0, ?(1, null)); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - public func take(self : List, n : Nat) : List = ( - func go(n : Nat, list : List, acc : List) : List = if (n == 0) reverse acc else switch list { - case (?(h, t)) go(n - 1 : Nat, t, ?(h, acc)); - case null reverse acc - } - )(n, self, null); - - /// Drop the first `n` elements from the given list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.drop(list, 2) == ?(2, null); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(1) - public func drop(self : List, n : Nat) : List = if (n == 0) self else switch self { - case (?(_, t)) drop(t, n - 1 : Nat); - case null null - }; - - /// Collapses the elements in `list` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.foldLeft( - /// list, - /// "", - /// func (acc, x) = acc # Nat.toText(x) - /// ) == "123"; - /// } - /// ``` - /// - /// Runtime: O(size(list)) - /// - /// Space: O(1) heap, O(1) stack - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(self : List, base : A, combine : (A, T) -> A) : A = switch self { - case null base; - case (?(h, t)) foldLeft(t, combine(base, h), combine) - }; - - /// Collapses the elements in `buffer` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.foldRight( - /// list, - /// "", - /// func (x, acc) = Nat.toText(x) # acc - /// ) == "123"; - /// } - /// ``` - /// - /// Runtime: O(size(list)) - /// - /// Space: O(1) heap, O(size(list)) stack - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldRight(self : List, base : A, combine : (T, A) -> A) : A = ( - func go(list : List, base : A, combine : (T, A) -> A) : A = switch list { - case null base; - case (?(h, t)) go(t, combine(h, base), combine) - } - )(reverse self, base, combine); - - /// Return the first element for which the given predicate `f` is true, - /// if such an element exists. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.find(list, func n = n > 1) == ?2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func find(self : List, f : T -> Bool) : ?T = switch self { - case null null; - case (?(h, t)) if (f h) ?h else find(t, f) - }; - - /// Return the first index for which the given predicate `f` is true. - /// If no element satisfies the predicate, returns null. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.fromArray(['A', 'B', 'C', 'D']); - /// let found = List.findIndex(list, func(x) { x == 'C' }); - /// assert found == ?2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func findIndex(self : List, f : T -> Bool) : ?Nat { - findIndex_(self, 0, f) - }; - - private func findIndex_(self : List, index : Nat, f : T -> Bool) : ?Nat = switch self { - case null null; - case (?(h, t)) if (f h) ?index else findIndex_(t, index + 1, f) - }; - - /// Return true if the given predicate `f` is true for all list - /// elements. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert not List.all(list, func n = n > 1); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func all(self : List, f : T -> Bool) : Bool = switch self { - case null true; - case (?(h, t)) f h and all(t, f) - }; - - /// Return true if there exists a list element for which - /// the given predicate `f` is true. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.any(list, func n = n > 1); - /// } - /// ``` - /// - /// Runtime: O(size(list)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func any(self : List, f : T -> Bool) : Bool = switch self { - case null false; - case (?(h, t)) f h or any(t, f) - }; - - /// Merge two ordered lists into a single ordered list. - /// This function requires both list to be ordered as specified - /// by the given relation `compare`. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list1 = ?(1, ?(2, ?(4, null))); - /// let list2 = ?(2, ?(4, ?(6, null))); - /// assert List.merge(list1, list2, Nat.compare) == ?(1, ?(2, ?(2, ?(4, ?(4, ?(6, null)))))); - /// } - /// ``` - /// - /// Runtime: O(size(l1) + size(l2)) - /// - /// Space: O(size(l1) + size(l2)) - /// - /// *Runtime and space assumes that `lessThanOrEqual` runs in O(1) time and space. - public func merge(self : List, other : List, compare : (implicit : (T, T) -> Order.Order)) : List = ( - func go(list1 : List, list2 : List, compare : (T, T) -> Order.Order, acc : List) : List = switch (list1, list2) { - case ((null, l) or (l, null)) reverse(revAppend(l, acc)); - case (?(h1, t1), ?(h2, t2)) switch (compare(h1, h2)) { - case (#less or #equal) go(t1, list2, compare, ?(h1, acc)); - case (#greater) go(list1, t2, compare, ?(h2, acc)) - } - } - )(self, other, compare, null); - - /// Check if two lists are equal using the given equality function to compare elements. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list1 = ?(1, ?(2, null)); - /// let list2 = ?(1, ?(2, null)); - /// assert List.equal(list1, list2, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equalItem` runs in O(1) time and space. - public func equal(self : List, other : List, equalItem : (implicit : (equal : (T, T) -> Bool))) : Bool = switch (self, other) { - case (null, null) true; - case (?(h1, t1), ?(h2, t2)) equalItem(h1, h2) and equal(t1, t2, equalItem); - case _ false - }; - - /// Compare two lists using lexicographic ordering specified by argument function `compareItem`. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list1 = ?(1, ?(2, null)); - /// let list2 = ?(3, ?(4, null)); - /// assert List.compare(list1, list2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: O(size(l1)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that argument `compare` runs in O(1) time and space. - public func compare(self : List, other : List, compareItem : (implicit : (compare : (T, T) -> Order.Order))) : Order.Order = switch (self, other) { - case (?(h1, t1), ?(h2, t2)) switch (compareItem(h1, h2)) { - case (#equal) compare(t1, t2, compareItem); - case o o - }; - case (null, null) #equal; - case (null, _) #less; - case _ #greater - }; - - /// Generate a list based on a length and a function that maps from - /// a list index to a list element. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.tabulate(3, func n = n * 2); - /// assert list == ?(0, ?(2, ?(4, null))); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func tabulate(n : Nat, f : Nat -> T) : List { - var i = 0; - var l : List = null; - while (i < n) { - l := ?(f i, l); - i += 1 - }; - reverse l - }; - - /// Create a list with exactly one element. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// assert List.singleton(0) == ?(0, null); - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func singleton(item : T) : List = ?(item, null); - - /// Create a list of the given length with the same value in each position. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.repeat('a', 3); - /// assert list == ?('a', ?('a', ?('a', null))); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - public func repeat(item : T, n : Nat) : List { - var res : List = null; - var i : Int = n; - while (i != 0) { - i -= 1; - res := ?(item, res) - }; - res - }; - - /// Create a list of pairs from a pair of lists. - /// - /// If the given lists have different lengths, then the created list will have a - /// length equal to the length of the smaller list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list1 = ?(0, ?(1, ?(2, null))); - /// let list2 = ?("0", ?("1", null)); - /// assert List.zip(list1, list2) == ?((0, "0"), ?((1, "1"), null)); - /// } - /// ``` - /// - /// Runtime: O(min(size(xs), size(ys))) - /// - /// Space: O(min(size(xs), size(ys))) - public func zip(self : List, other : List) : List<(T, U)> = zipWith(self, other, func(x, y) = (x, y)); - - /// Create a list in which elements are created by applying function `f` to each pair `(x, y)` of elements - /// occuring at the same position in list `xs` and list `ys`. - /// - /// If the given lists have different lengths, then the created list will have a - /// length equal to the length of the smaller list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// import Char "mo:core/Char"; - /// - /// persistent actor { - /// let list1 = ?(0, ?(1, ?(2, null))); - /// let list2 = ?('a', ?('b', null)); - /// assert List.zipWith( - /// list1, - /// list2, - /// func (n, c) = Nat.toText(n) # Char.toText(c) - /// ) == ?("0a", ?("1b", null)); - /// } - /// ``` - /// - /// Runtime: O(min(size(xs), size(ys))) - /// - /// Space: O(min(size(xs), size(ys))) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func zipWith(self : List, other : List, f : (T, U) -> V) : List = ( - func go(list1 : List, list2 : List, f : (T, U) -> V, acc : List) : List = switch (list1, list2) { - case ((null, _) or (_, null)) reverse acc; - case (?(h1, t1), ?(h2, t2)) go(t1, t2, f, ?(f(h1, h2), acc)) - } - )(self, other, f, null); - - /// Split the given list at the given zero-based index. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.split(list, 2) == (?(0, ?(1, null)), ?(2, null)); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - public func split(self : List, n : Nat) : (List, List) { - func go(n : Nat, list : List, acc : List) : (List, List) = if (n == 0) (reverse acc, list) else switch list { - case (?(h, t)) go(n - 1 : Nat, t, ?(h, acc)); - case null (reverse acc, null) - }; - go(n, self, null) - }; - - /// Split the given list into chunks of length `n`. - /// The last chunk will be shorter if the length of the given list - /// does not divide by `n` evenly. Traps if `n` = 0. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// assert List.chunks(list, 2) == ?(?(0, ?(1, null)), ?(?(2, ?(3, null)), ?(?(4, null), null))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func chunks(self : List, n : Nat) : List> { - if (n == 0) trap "pure/List.chunks()"; - func go(list : List, n : Nat, acc : List>) : List> = switch (split(list, n)) { - case (null, _) reverse acc; - case (pre, null) reverse(?(pre, acc)); - case (pre, post) go(post, n, ?(pre, acc)) - }; - go(self, n, null) - }; - - /// Returns an iterator to the elements in the list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = List.fromArray([3, 1, 4]); - /// var text = ""; - /// for (item in List.values(list)) { - /// text #= Nat.toText(item); - /// }; - /// assert text == "314"; - /// } - /// ``` - public func values(self : List) : Iter.Iter = object { - var l = self; - public func next() : ?T = switch l { - case null null; - case (?(h, t)) { - l := t; - ?h - } - } - }; - - /// Returns an iterator to the `(index, element)` pairs in the list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = List.fromArray([3, 1, 4]); - /// var text = ""; - /// for ((index, element) in List.enumerate(list)) { - /// text #= Nat.toText(index); - /// }; - /// assert text == "012"; - /// } - /// ``` - public func enumerate(self : List) : Iter.Iter<(Nat, T)> = object { - var i = 0; - var l = self; - public func next() : ?(Nat, T) = switch l { - case null null; - case (?(h, t)) { - l := t; - let index = i; - i += 1; - ?(index, h) - } - } - }; - - /// Convert an array into a list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.fromArray([0, 1, 2, 3, 4]); - /// assert list == ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromArray(array : [T]) : List { - func go(from : Nat) : List = if (from < array.size()) ?(array.get from, go(from + 1)) else null; - go 0 - }; - - /// Convert a mutable array into a list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.fromVarArray([var 0, 1, 2, 3, 4]); - /// assert list == ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromVarArray(array : [var T]) : List = fromArray(Array.fromVarArray(array)); - - /// Create an array from a list. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Array "mo:core/Array"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let array = List.toArray(?(0, ?(1, ?(2, ?(3, ?(4, null)))))); - /// assert Array.equal(array, [0, 1, 2, 3, 4], Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toArray(self : List) : [T] { - var l = self; - Array_tabulate(size self, func _ { let ?(h, t) = l else Runtime.trap("List.toArray(): unreachable"); l := t; h }) - }; - - /// Create a mutable array from a list. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Array "mo:core/Array"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let array = List.toVarArray(?(0, ?(1, ?(2, ?(3, ?(4, null)))))); - /// assert Array.equal(Array.fromVarArray(array), [0, 1, 2, 3, 4], Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toVarArray(self : List) : [var T] = Array.toVarArray(toArray(self)); - - /// Create a list from an iterator, consuming the iterator. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.fromIter([0, 1, 2, 3, 4].vals()); - /// assert list == ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromIter(iter : Iter.Iter) : List { - var result : List = null; - for (x in iter) { - result := ?(x, result) - }; - reverse result - }; - - /// Convert an iterator to a list, consuming the iterator. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// transient let iter = [0, 1, 2, 3, 4].vals(); - /// - /// let list = iter.toList(); - /// - /// assert list == ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toList(self : Iter.Iter) : List { - fromIter(self) - }; - - /// Convert a list to a text representation using the provided function to convert each element to text. - /// The resulting text will be in the format "[element1, element2, ...]". - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.toText(list, Nat.toText) == "PureList[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toText(self : List, f : (implicit : T -> Text)) : Text { - var text = "PureList["; - var first = true; - forEach( - self, - func(item : T) { - if first { - first := false - } else { - text #= ", " - }; - text #= f item - } - ); - text # "]" - }; - - // revAppend([x1 .. xn], [y1 .. ym]) = [xn .. x1, y1 .. ym] - func revAppend(l : List, m : List) : List = switch l { - case (?(h, t)) revAppend(t, ?(h, m)); - case null m - } -} diff --git a/.mops/core@2.4.0/src/pure/Map.mo b/.mops/core@2.4.0/src/pure/Map.mo deleted file mode 100644 index ebddab3..0000000 --- a/.mops/core@2.4.0/src/pure/Map.mo +++ /dev/null @@ -1,1563 +0,0 @@ -/// Immutable, ordered key-value maps. -/// -/// The map type is stable whenever the key and value types are stable, allowing -/// map values to be stored in stable variables. -/// -/// Keys are ordered by an explicit `compare` function, which *must* be the same -/// across all operations on a given map. -/// -/// -/// Example: -/// ```motoko -/// import Map "mo:core/pure/Map"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// // creation -/// let empty = Map.empty(); -/// // insertion -/// let map1 = Map.add(empty, Nat.compare, 0, "Zero"); -/// // retrieval -/// assert Map.get(empty, Nat.compare, 0) == null; -/// assert Map.get(map1, Nat.compare, 0) == ?"Zero"; -/// // removal -/// let map2 = Map.remove(map1, Nat.compare, 0); -/// assert not Map.isEmpty(map1); -/// assert Map.isEmpty(map2); -/// } -/// ``` -/// -/// The internal representation is a red-black tree. -/// -/// A red-black tree is a balanced binary search tree ordered by the keys. -/// -/// The tree data structure internally colors each of its nodes either red or black, -/// and uses this information to balance the tree during the modifying operations. -/// -/// Performance: -/// * Runtime: `O(log(n))` worst case cost per insertion, removal, and retrieval operation. -/// * Space: `O(n)` for storing the entire tree. -/// `n` denotes the number of key-value entries (i.e. nodes) stored in the tree. -/// -/// Note: -/// * Map operations, such as retrieval, insertion, and removal create `O(log(n))` temporary objects that become garbage. -/// -/// Credits: -/// -/// The core of this implementation is derived from: -/// -/// * Ken Friis Larsen's [RedBlackMap.sml](https://github.com/kfl/mosml/blob/master/src/mosmllib/Redblackmap.sml), which itself is based on: -/// * Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](http://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html). - -import Order "../Order"; -import Iter "../Iter"; -import Types "../Types"; -import Runtime "../Runtime"; - -// TODO: inline Internal? -// TODO: Do we want clone or clear, just to match imperative API? -// inline Tree type, remove Types.Pure.Tree? - -module { - - /// @deprecated M0235 - public type Map = Types.Pure.Map; - - type Tree = Types.Pure.Map.Tree; - - /// Create a new empty immutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// assert Map.size(map) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Map { - Internal.empty() - }; - - /// Determines whether a key-value map is empty. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map0 = Map.empty(); - /// let map1 = Map.add(map0, Nat.compare, 0, "Zero"); - /// - /// assert Map.isEmpty(map0); - /// assert not Map.isEmpty(map1); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func isEmpty(self : Map) : Bool { - self.size == 0 - }; - - /// Determine the size of the map as the number of key-value entries. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.size(map) == 3; - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func size(self : Map) : Nat = self.size; - - /// Test whether the map `map`, ordered by `compare`, contains a binding for the given `key`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.containsKey(map, Nat.compare, 1); - /// assert not Map.containsKey(map, Nat.compare, 42); - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func containsKey(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : Bool = Internal.contains(self.root, compare, key); - - /// Given, `map` ordered by `compare`, return the value associated with key `key` if present and `null` otherwise. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.get(map, Nat.compare, 1) == ?"One"; - /// assert Map.get(map, Nat.compare, 42) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func get(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : ?V = Internal.get(self.root, compare, key); - - /// Given `map` ordered by `compare`, insert a mapping from `key` to `value`. - /// Returns the modified map and `true` if the key is new to map, otherwise `false`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = Map.empty(); - /// - /// do { - /// let (map1, new1) = Map.insert(map0, Nat.compare, 0, "Zero"); - /// assert Iter.toArray(Map.entries(map1)) == [(0, "Zero")]; - /// assert new1; - /// let (map2, new2) = Map.insert(map1, Nat.compare, 0, "Nil"); - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Nil")]; - /// assert not new2 - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `m := Map.add(m, cmp, k, v)`) - /// causes collecting `O(log(n))` nodes. - public func insert(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : (Map, Bool) { - switch (swap(self, compare, key, value)) { - case (map1, null) (map1, true); - case (map1, _) (map1, false) - } - }; - - /// Given `map` ordered by `compare`, add a new mapping from `key` to `value`. - /// Replaces any existing entry with key `key`. - /// Returns the modified map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// var map = Map.empty(); - /// - /// map := Map.add(map, Nat.compare, 0, "Zero"); - /// map := Map.add(map, Nat.compare, 1, "One"); - /// map := Map.add(map, Nat.compare, 0, "Nil"); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Nil"), (1, "One")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `m := Map.add(m, cmp, k, v)`) - /// causes collecting `O(log(n))` nodes. - public func add(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : Map { - swap(self, compare, key, value).0 - }; - - /// Given `map` ordered by `compare`, add a mapping from `key` to `value`. Overwrites any existing entry with key `key`. - /// Returns the modified map and the previous value associated with key `key` - /// or `null` if no such value exists. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// do { - /// let (map1, old1) = Map.swap(map0, Nat.compare, 0, "Nil"); - /// assert Iter.toArray(Map.entries(map1)) == [(0, "Nil"), (1, "One"), (2, "Two")]; - /// assert old1 == ?"Zero"; - /// - /// let (map2, old2) = Map.swap(map0, Nat.compare, 3, "Three"); - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Zero"), (1, "One"), (2, "Two"), (3, "Three")]; - /// assert old2 == null; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `m := Map.swap(m, Nat.compare, k, v).0`) - /// causes collecting `O(log(n))` nodes. - public func swap(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : (Map, ?V) { - switch (Internal.swap(self.root, compare, key, value)) { - case (t, null) { ({ root = t; size = self.size + 1 }, null) }; - case (t, v) { ({ root = t; size = self.size }, v) } - } - }; - - /// Overwrites the value of an existing key and returns the updated map and previous value. - /// If the key does not exist, returns the original map and `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let singleton = Map.singleton(0, "Zero"); - /// - /// do { - /// let (map1, prev1) = Map.replace(singleton, Nat.compare, 0, "Nil"); // overwrites the value for existing key. - /// assert prev1 == ?"Zero"; - /// assert Map.get(map1, Nat.compare, 0) == ?"Nil"; - /// - /// let (map2, prev2) = Map.replace(map1, Nat.compare, 1, "One"); // no effect, key is absent - /// assert prev2 == null; - /// assert Map.get(map2, Nat.compare, 1) == null; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func replace(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : (Map, ?V) { - // TODO: Could be optimized in future - if (containsKey(self, compare, key)) { - swap(self, compare, key, value) - } else { (self, null) } - }; - - /// Given a `map`, ordered by `compare`, deletes any entry for `key` from `map`. - /// Has no effect if `key` is not present in the map. - /// Returns the updated map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = - /// Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// let map1 = Map.remove(map0, Nat.compare, 1); - /// assert Iter.toArray(Map.entries(map1)) == [(0, "Zero"), (2, "Two")]; - /// let map2 = Map.remove(map0, Nat.compare, 42); - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `map := Map.delete(map, compare, k).0`) - /// causes collecting `O(log(n))` nodes. - public func remove(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : Map { - switch (Internal.remove(self.root, compare, key)) { - case (_, null) self; - case (t, ?_) { { root = t; size = self.size - 1 } } - } - }; - - /// Given a `map`, ordered by `compare`, deletes any entry for `key` from `map`. - /// Has no effect if `key` is not present in the map. - /// Returns the updated map and `true` if the `key` was present in `map`, otherwise `false`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = - /// Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// do { - /// let (map1, pres1) = Map.delete(map0, Nat.compare, 1); - /// assert Iter.toArray(Map.entries(map1)) == [(0, "Zero"), (2, "Two")]; - /// assert pres1; - /// let (map2, pres2) = Map.delete(map0, Nat.compare, 42); - /// assert not pres2; - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `map := Map.delete(map, compare, k).0`) - /// causes collecting `O(log(n))` nodes. - public func delete(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : (Map, Bool) { - switch (Internal.remove(self.root, compare, key)) { - case (_, null) { (self, false) }; - case (t, ?_) { ({ root = t; size = self.size - 1 }, true) } - } - }; - - /// Given a `map`, ordered by `compare`, deletes the entry for `key`. Returns a modified map, leaving `map` unchanged, and the - /// previous value associated with `key` or `null` if no such value exists. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// do { - /// let (map1, prev1) = Map.take(map0, Nat.compare, 0); - /// assert Iter.toArray(Map.entries(map1)) == [(1, "One"), (2, "Two")]; - /// assert prev1 == ?"Zero"; - /// - /// let (map2, prev2) = Map.take(map0, Nat.compare, 42); - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// assert prev2 == null; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `map := Map.remove(map, compare, key)`) - /// causes collecting `O(log(n))` nodes. - public func take(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : (Map, ?V) { - switch (Internal.remove(self.root, compare, key)) { - case (t, null) { ({ root = t; size = self.size }, null) }; - case (t, v) { ({ root = t; size = self.size - 1 }, v) } - } - }; - - /// Given a `map` retrieves the key-value pair in `map` with a maximal key. If `map` is empty returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.maxEntry(map) == ?(2, "Two"); - /// assert Map.maxEntry(Map.empty()) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func maxEntry(self : Map) : ?(K, V) = Internal.maxEntry(self.root); - - /// Retrieves a key-value pair from `map` with the minimal key. If the map is empty returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.minEntry(map) == ?(0, "Zero"); - /// assert Map.minEntry(Map.empty()) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func minEntry(self : Map) : ?(K, V) = Internal.minEntry(self.root); - - /// Returns an Iterator (`Iter`) over the key-value pairs in the map. - /// Iterator provides a single method `next()`, which returns - /// pairs in ascending order by keys, or `null` when out of pairs to iterate over. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// var sum = 0; - /// var text = ""; - /// for ((k, v) in Map.entries(map)) { sum += k; text #= v }; - /// assert sum == 3; - /// assert text == "ZeroOneTwo" - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func entries(self : Map) : Iter.Iter<(K, V)> = Internal.iter(self.root, #fwd); - - /// Returns an Iterator (`Iter`) over the key-value pairs in the map. - /// Iterator provides a single method `next()`, which returns - /// pairs in descending order by keys, or `null` when out of pairs to iterate over. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.reverseEntries(map)) == [(2, "Two"), (1, "One"), (0, "Zero")]; - /// var sum = 0; - /// var text = ""; - /// for ((k, v) in Map.reverseEntries(map)) { sum += k; text #= v }; - /// assert sum == 3; - /// assert text == "TwoOneZero" - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func reverseEntries(self : Map) : Iter.Iter<(K, V)> = Internal.iter(self.root, #bwd); - - /// Given a `map`, returns an Iterator (`Iter`) over the keys of the `map`. - /// Iterator provides a single method `next()`, which returns - /// keys in ascending order, or `null` when out of keys to iterate over. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.keys(map)) == [0, 1, 2]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func keys(self : Map) : Iter.Iter = Iter.map(entries(self), func(kv : (K, V)) : K { kv.0 }); - - /// Given a `map`, returns an Iterator (`Iter`) over the values of the map. - /// Iterator provides a single method `next()`, which returns - /// values in ascending order of associated keys, or `null` when out of values to iterate over. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.values(map)) == ["Zero", "One", "Two"]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func values(self : Map) : Iter.Iter = Iter.map(entries(self), func(kv : (K, V)) : V { kv.1 }); - - /// Returns a new map, containing all entries given by the iterator `i`. - /// If there are multiple entries with the same key the last one is taken. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = - /// Iter.fromArray([(0, "Zero"), (2, "Two"), (1, "One")]); - /// - /// let map = Map.fromIter(iter, Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func fromIter(iter : Iter.Iter<(K, V)>, compare : (implicit : (K, K) -> Order.Order)) : Map = Internal.fromIter(iter, compare); - - /// Convert an iterator of entries into a map. - /// If there are multiple entries with the same key the last one is taken. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = - /// Iter.fromArray([(0, "Zero"), (2, "Two"), (1, "One")]); - /// - /// let map = iter.toMap(Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func toMap(self : Iter.Iter<(K, V)>, compare : (implicit : (K, K) -> Order.Order)) : Map = Internal.fromIter(self, compare); - - /// Given a `map` and function `f`, creates a new map by applying `f` to each entry in the map `m`. Each entry - /// `(k, v)` in the old map is transformed into a new entry `(k, v2)`, where - /// the new value `v2` is created by applying `f` to `(k, v)`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func f(key : Nat, _val : Text) : Nat = key * 2; - /// - /// let resMap = Map.map(map, f); - /// - /// assert Iter.toArray(Map.entries(resMap)) == [(0, 0), (1, 2), (2, 4)]; - /// } - /// ``` - /// - /// Cost of mapping all the elements: - /// Runtime: `O(n)`. - /// Space: `O(n)` retained memory - /// where `n` denotes the number of key-value entries stored in the map. - public func map(self : Map, f : (K, V1) -> V2) : Map = Internal.map(self, f); - - /// Collapses the elements in the `map` into a single value by starting with `base` - /// and progressively combining keys and values into `base` with `combine`. Iteration runs - /// left to right. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func folder(accum : (Nat, Text), key : Nat, val : Text) : ((Nat, Text)) - /// = (key + accum.0, accum.1 # val); - /// - /// assert Map.foldLeft(map, (0, ""), folder) == (3, "ZeroOneTwo"); - /// } - /// ``` - /// - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: depends on `combine` function plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func foldLeft( - self : Map, - base : A, - combine : (A, K, V) -> A - ) : A = Internal.foldLeft(self.root, base, combine); - - /// Collapses the elements in the `map` into a single value by starting with `base` - /// and progressively combining keys and values into `base` with `combine`. Iteration runs - /// right to left. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func folder(key : Nat, val : Text, accum : (Nat, Text)) : ((Nat, Text)) - /// = (key + accum.0, accum.1 # val); - /// - /// assert Map.foldRight(map, (0, ""), folder) == (3, "TwoOneZero"); - /// } - /// ``` - /// - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: depends on `combine` function plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func foldRight( - self : Map, - base : A, - combine : (K, V, A) -> A - ) : A = Internal.foldRight(self.root, base, combine); - - /// Test whether all key-value pairs in `map` satisfy the given predicate `pred`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "0"), (2, "2"), (1, "1")].values(), Nat.compare); - /// - /// assert Map.all(map, func (k, v) = v == Nat.toText(k)); - /// assert not Map.all(map, func (k, v) = k < 2); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func all(self : Map, pred : (K, V) -> Bool) : Bool = Internal.all(self.root, pred); - - /// Test if any key-value pair in `map` satisfies the given predicate `pred`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "0"), (2, "2"), (1, "1")].values(), Nat.compare); - /// - /// assert Map.any(map, func (k, v) = (k >= 0)); - /// assert not Map.any(map, func (k, v) = (k >= 3)); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func any(self : Map, pred : (K, V) -> Bool) : Bool = Internal.any(self.root, pred); - - /// Create a new immutable key-value `map` with a single entry. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.singleton(0, "Zero"); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero")]; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func singleton(key : K, value : V) : Map { - { - size = 1; - root = #red(#leaf, key, value, #leaf) - } - }; - - /// Apply an operation for each key-value pair contained in the map. - /// The operation is applied in ascending order of the keys. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// var sum = 0; - /// var text = ""; - /// Map.forEach(map, func (key, value) { - /// sum += key; - /// text #= value; - /// }); - /// assert sum == 3; - /// assert text == "ZeroOneTwo"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - public func forEach(self : Map, operation : (K, V) -> ()) = Internal.forEach(self, operation); - - /// Filter entries in a new map. - /// Returns a new map that only contains the key-value pairs - /// that fulfil the criterion function. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numberNames = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// let evenNames = Map.filter(numberNames, Nat.compare, func (key, value) { - /// key % 2 == 0 - /// }); - /// - /// assert Iter.toArray(Map.entries(evenNames)) == [(0, "Zero"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func filter(self : Map, compare : (implicit : (K, K) -> Order.Order), criterion : (K, V) -> Bool) : Map = Internal.filter(self, compare, criterion); - - /// Given a `map`, comparison `compare` and function `f`, - /// constructs a new map ordered by `compare`, by applying `f` to each entry in `map`. - /// For each entry `(k, v)` in the old map, if `f` evaluates to `null`, the entry is discarded. - /// Otherwise, the entry is transformed into a new entry `(k, v2)`, where - /// the new value `v2` is the result of applying `f` to `(k, v)`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func f(key : Nat, val : Text) : ?Text { - /// if(key == 0) {null} - /// else { ?("Twenty " # val)} - /// }; - /// - /// let newMap = Map.filterMap(map, Nat.compare, f); - /// - /// assert Iter.toArray(Map.entries(newMap)) == [(1, "Twenty One"), (2, "Twenty Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func filterMap(self : Map, compare : (implicit : (K, K) -> Order.Order), f : (K, V1) -> ?V2) : Map = Internal.mapFilter(self, compare : (K, K) -> Order.Order, f); - - /// Validate the representation invariants of the given `map`. - /// Assert if any invariants are violated. - public func assertValid(self : Map, compare : (implicit : (K, K) -> Order.Order)) : () = Internal.validate(self, compare); - - /// Converts the `map` to its textual representation using `keyFormat` and `valueFormat` to convert each key and value to `Text`. - /// - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// assert Map.toText(map, Nat.toText, func t { t }) == "PureMap{(0, Zero), (1, One), (2, Two)}"; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `keyFormat` and `valueFormat` run in O(1) time and space. - public func toText(self : Map, keyFormat : (implicit : (toText : K -> Text)), valueFormat : (implicit : (toText : V -> Text))) : Text { - var text = "PureMap{"; - var sep = ""; - for ((k, v) in entries(self)) { - text #= sep # "(" # keyFormat(k) # ", " # valueFormat(v) # ")"; - sep := ", " - }; - text # "}" - }; - - /// Test whether two immutable maps have equal entries. - /// Assumes both maps are ordered equivalently. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let map1 = Map.fromIter([(0, "Zero"), (1, "One"), (2, "Two")].values(), Nat.compare); - /// let map2 = Map.fromIter([(2, "Two"), (1, "One"), (0, "Zero")].values(), Nat.compare); - /// assert(Map.equal(map1, map2, Nat.compare, Text.equal)); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func equal(self : Map, other : Map, compare : (implicit : (K, K) -> Order.Order), equal : (implicit : (V, V) -> Bool)) : Bool { - if (self.size != other.size) { - return false - }; - let iterator1 = entries(self); - let iterator2 = entries(other); - loop { - let next1 = iterator1.next(); - let next2 = iterator2.next(); - switch (next1, next2) { - case (null, null) { - return true - }; - case (?(key1, value1), ?(key2, value2)) { - if (not (compare(key1, key2) == #equal) or not equal(value1, value2)) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Compare two maps by primarily comparing keys and secondarily values. - /// Both maps are iterated by the ascending order of their creation and - /// order is determined by the following rules: - /// Less: - /// `map1` is less than `map2` if: - /// * the pairwise iteration hits a entry pair `entry1` and `entry2` where - /// `entry1` is less than `entry2` and all preceding entry pairs are equal, or, - /// * `map1` is a strict prefix of `map2`, i.e. `map2` has more entries than `map1` - /// and all entries of `map1` occur at the beginning of iteration `map2`. - /// `entry1` is less than `entry2` if: - /// * the key of `entry1` is less than the key of `entry2`, or - /// * `entry1` and `entry2` have equal keys and the value of `entry1` is less than - /// the value of `entry2`. - /// Equal: - /// `map1` and `map2` have same series of equal entries by pairwise iteration. - /// Greater: - /// `map1` is neither less nor equal `map2`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let map1 = Map.fromIter([(0, "Zero"), (1, "One")].values(), Nat.compare); - /// let map2 = Map.fromIter([(0, "Zero"), (2, "Two")].values(), Nat.compare); - /// - /// assert Map.compare(map1, map2, Nat.compare, Text.compare) == #less; - /// assert Map.compare(map1, map1, Nat.compare, Text.compare) == #equal; - /// assert Map.compare(map2, map1, Nat.compare, Text.compare) == #greater - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that `compareKey` and `compareValue` have runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func compare(self : Map, other : Map, compareKey : (implicit : (compare : (K, K) -> Order.Order)), compareValue : (implicit : (compare : (V, V) -> Order.Order))) : Order.Order { - let iterator1 = entries(self); - let iterator2 = entries(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?(key1, value1), ?(key2, value2)) { - let keyComparison = compareKey(key1, key2); - if (keyComparison != #equal) { - return keyComparison - }; - let valueComparison = compareValue(value1, value2); - if (valueComparison != #equal) { - return valueComparison - } - } - } - } - }; - - module Internal { - - public func empty() : Map { { size = 0; root = #leaf } }; - - public func fromIter(i : Iter.Iter<(K, V)>, compare : (K, K) -> Order.Order) : Map { - var map = #leaf : Tree; - var size = 0; - for (val in i) { - map := add(map, compare, val.0, val.1); - size += 1 - }; - { root = map; size } - }; - - type List = Types.Pure.List; - - type IterRep = List<{ #tr : Tree; #xy : (K, V) }>; - - public func iter(map : Tree, direction : { #fwd; #bwd }) : Iter.Iter<(K, V)> { - let turnLeftFirst : MapTraverser = func(l, x, y, r, ts) { - ?(#tr(l), ?(#xy(x, y), ?(#tr(r), ts))) - }; - - let turnRightFirst : MapTraverser = func(l, x, y, r, ts) { - ?(#tr(r), ?(#xy(x, y), ?(#tr(l), ts))) - }; - - switch direction { - case (#fwd) IterMap(map, turnLeftFirst); - case (#bwd) IterMap(map, turnRightFirst) - } - }; - - type MapTraverser = (Tree, K, V, Tree, IterRep) -> IterRep; - - class IterMap(tree : Tree, mapTraverser : MapTraverser) { - var trees : IterRep = ?(#tr(tree), null); - public func next() : ?(K, V) { - switch (trees) { - case (null) { null }; - case (?(#tr(#leaf), ts)) { - trees := ts; - next() - }; - case (?(#xy(xy), ts)) { - trees := ts; - ?xy - }; - case (?(#tr(#red(l, x, y, r)), ts)) { - trees := mapTraverser(l, x, y, r, ts); - next() - }; - case (?(#tr(#black(l, x, y, r)), ts)) { - trees := mapTraverser(l, x, y, r, ts); - next() - } - } - } - }; - - public func map(map : Map, f : (K, V1) -> V2) : Map { - func mapRec(m : Tree) : Tree { - switch m { - case (#leaf) { #leaf }; - case (#red(l, x, y, r)) { - #red(mapRec l, x, f(x, y), mapRec r) - }; - case (#black(l, x, y, r)) { - #black(mapRec l, x, f(x, y), mapRec r) - } - } - }; - { size = map.size; root = mapRec(map.root) } - }; - - public func foldLeft( - map : Tree, - base : Accum, - combine : (Accum, Key, Value) -> Accum - ) : Accum { - switch (map) { - case (#leaf) { base }; - case (#red(l, k, v, r)) { - let left = foldLeft(l, base, combine); - let middle = combine(left, k, v); - foldLeft(r, middle, combine) - }; - case (#black(l, k, v, r)) { - let left = foldLeft(l, base, combine); - let middle = combine(left, k, v); - foldLeft(r, middle, combine) - } - } - }; - - public func foldRight( - map : Tree, - base : Accum, - combine : (Key, Value, Accum) -> Accum - ) : Accum { - switch (map) { - case (#leaf) { base }; - case (#red(l, k, v, r)) { - let right = foldRight(r, base, combine); - let middle = combine(k, v, right); - foldRight(l, middle, combine) - }; - case (#black(l, k, v, r)) { - let right = foldRight(r, base, combine); - let middle = combine(k, v, right); - foldRight(l, middle, combine) - } - } - }; - - public func forEach(map : Map, operation : (K, V) -> ()) { - func combine(_acc : Null, key : K, value : V) : Null { - operation(key, value); - null - }; - ignore foldLeft(map.root, null, combine) - }; - - public func filter(map : Map, compare : (K, K) -> Order.Order, criterion : (K, V) -> Bool) : Map { - var size = 0; - func combine(acc : Tree, key : K, value : V) : Tree { - if (criterion(key, value)) { - size += 1; - add(acc, compare, key, value) - } else acc - }; - { root = foldLeft(map.root, #leaf, combine); size } - }; - - public func mapFilter(map : Map, compare : (K, K) -> Order.Order, f : (K, V1) -> ?V2) : Map { - var size = 0; - func combine(acc : Tree, key : K, value1 : V1) : Tree { - switch (f(key, value1)) { - case null { acc }; - case (?value2) { - size += 1; - add(acc, compare, key, value2) - } - } - }; - { root = foldLeft(map.root, #leaf, combine); size } - }; - - public func get(t : Tree, compare : (K, K) -> Order.Order, x : K) : ?V { - switch t { - case (#red(l, x1, y1, r)) { - switch (compare(x, x1)) { - case (#less) { get(l, compare, x) }; - case (#equal) { ?y1 }; - case (#greater) { get(r, compare, x) } - } - }; - case (#black(l, x1, y1, r)) { - switch (compare(x, x1)) { - case (#less) { get(l, compare, x) }; - case (#equal) { ?y1 }; - case (#greater) { get(r, compare, x) } - } - }; - case (#leaf) { null } - } - }; - - public func contains(m : Tree, compare : (K, K) -> Order.Order, key : K) : Bool { - switch (get(m, compare, key)) { - case (null) { false }; - case (_) { true } - } - }; - - public func maxEntry(m : Tree) : ?(K, V) { - func rightmost(m : Tree) : (K, V) { - switch m { - case (#red(_, k, v, #leaf)) { (k, v) }; - case (#red(_, _, _, r)) { rightmost(r) }; - case (#black(_, k, v, #leaf)) { (k, v) }; - case (#black(_, _, _, r)) { rightmost(r) }; - case (#leaf) { Runtime.trap "pure/Map.maxEntry() impossible" } - } - }; - switch m { - case (#leaf) { null }; - case (_) { ?rightmost(m) } - } - }; - - public func minEntry(m : Tree) : ?(K, V) { - func leftmost(m : Tree) : (K, V) { - switch m { - case (#red(#leaf, k, v, _)) { (k, v) }; - case (#red(l, _, _, _)) { leftmost(l) }; - case (#black(#leaf, k, v, _)) { (k, v) }; - case (#black(l, _, _, _)) { leftmost(l) }; - case (#leaf) { Runtime.trap "pure/Map.minEntry() impossible" } - } - }; - switch m { - case (#leaf) { null }; - case (_) { ?leftmost(m) } - } - }; - - public func all(m : Tree, pred : (K, V) -> Bool) : Bool { - switch m { - case (#red(l, k, v, r)) { - pred(k, v) and all(l, pred) and all(r, pred) - }; - case (#black(l, k, v, r)) { - pred(k, v) and all(l, pred) and all(r, pred) - }; - case (#leaf) { true } - } - }; - - public func any(m : Tree, pred : (K, V) -> Bool) : Bool { - switch m { - case (#red(l, k, v, r)) { - pred(k, v) or any(l, pred) or any(r, pred) - }; - case (#black(l, k, v, r)) { - pred(k, v) or any(l, pred) or any(r, pred) - }; - case (#leaf) { false } - } - }; - - func redden(t : Tree) : Tree { - switch t { - case (#black(l, x, y, r)) { (#red(l, x, y, r)) }; - case _ { - Runtime.trap "pure/Map.redden() impossible" - } - } - }; - - func lbalance(left : Tree, x : K, y : V, right : Tree) : Tree { - switch (left, right) { - case (#red(#red(l1, x1, y1, r1), x2, y2, r2), r) { - #red( - #black(l1, x1, y1, r1), - x2, - y2, - #black(r2, x, y, r) - ) - }; - case (#red(l1, x1, y1, #red(l2, x2, y2, r2)), r) { - #red( - #black(l1, x1, y1, l2), - x2, - y2, - #black(r2, x, y, r) - ) - }; - case _ { - #black(left, x, y, right) - } - } - }; - - func rbalance(left : Tree, x : K, y : V, right : Tree) : Tree { - switch (left, right) { - case (l, #red(l1, x1, y1, #red(l2, x2, y2, r2))) { - #red( - #black(l, x, y, l1), - x1, - y1, - #black(l2, x2, y2, r2) - ) - }; - case (l, #red(#red(l1, x1, y1, r1), x2, y2, r2)) { - #red( - #black(l, x, y, l1), - x1, - y1, - #black(r1, x2, y2, r2) - ) - }; - case _ { - #black(left, x, y, right) - } - } - }; - - type ClashResolver = { old : A; new : A } -> A; - - func insertWith( - m : Tree, - compare : (K, K) -> Order.Order, - key : K, - val : V, - onClash : ClashResolver - ) : Tree { - func ins(tree : Tree) : Tree { - switch tree { - case (#black(left, x, y, right)) { - switch (compare(key, x)) { - case (#less) { - lbalance(ins left, x, y, right) - }; - case (#greater) { - rbalance(left, x, y, ins right) - }; - case (#equal) { - let newVal = onClash({ new = val; old = y }); - #black(left, key, newVal, right) - } - } - }; - case (#red(left, x, y, right)) { - switch (compare(key, x)) { - case (#less) { - #red(ins left, x, y, right) - }; - case (#greater) { - #red(left, x, y, ins right) - }; - case (#equal) { - let newVal = onClash { new = val; old = y }; - #red(left, key, newVal, right) - } - } - }; - case (#leaf) { - #red(#leaf, key, val, #leaf) - } - } - }; - switch (ins m) { - case (#red(left, x, y, right)) { - #black(left, x, y, right) - }; - case other { other } - } - }; - - public func swap( - m : Tree, - compare : (K, K) -> Order.Order, - key : K, - val : V - ) : (Tree, ?V) { - var oldVal : ?V = null; - func onClash(clash : { old : V; new : V }) : V { - oldVal := ?clash.old; - clash.new - }; - let res = insertWith(m, compare, key, val, onClash); - (res, oldVal) - }; - - public func add( - m : Tree, - compare : (K, K) -> Order.Order, - key : K, - val : V - ) : Tree = swap(m, compare, key, val).0; - - func balLeft(left : Tree, x : K, y : V, right : Tree) : Tree { - switch (left, right) { - case (#red(l1, x1, y1, r1), r) { - #red( - #black(l1, x1, y1, r1), - x, - y, - r - ) - }; - case (_, #black(l2, x2, y2, r2)) { - rbalance(left, x, y, #red(l2, x2, y2, r2)) - }; - case (_, #red(#black(l2, x2, y2, r2), x3, y3, r3)) { - #red( - #black(left, x, y, l2), - x2, - y2, - rbalance(r2, x3, y3, redden r3) - ) - }; - case _ { Runtime.trap "pure/Map.balLeft() impossible" } - } - }; - - func balRight(left : Tree, x : K, y : V, right : Tree) : Tree { - switch (left, right) { - case (l, #red(l1, x1, y1, r1)) { - #red( - l, - x, - y, - #black(l1, x1, y1, r1) - ) - }; - case (#black(l1, x1, y1, r1), r) { - lbalance(#red(l1, x1, y1, r1), x, y, r) - }; - case (#red(l1, x1, y1, #black(l2, x2, y2, r2)), r3) { - #red( - lbalance(redden l1, x1, y1, l2), - x2, - y2, - #black(r2, x, y, r3) - ) - }; - case _ { Runtime.trap "pure/Map.balRight() impossible" } - } - }; - - func append(left : Tree, right : Tree) : Tree { - switch (left, right) { - case (#leaf, _) { right }; - case (_, #leaf) { left }; - case ( - #red(l1, x1, y1, r1), - #red(l2, x2, y2, r2) - ) { - switch (append(r1, l2)) { - case (#red(l3, x3, y3, r3)) { - #red( - #red(l1, x1, y1, l3), - x3, - y3, - #red(r3, x2, y2, r2) - ) - }; - case r1l2 { - #red(l1, x1, y1, #red(r1l2, x2, y2, r2)) - } - } - }; - case (t1, #red(l2, x2, y2, r2)) { - #red(append(t1, l2), x2, y2, r2) - }; - case (#red(l1, x1, y1, r1), t2) { - #red(l1, x1, y1, append(r1, t2)) - }; - case (#black(l1, x1, y1, r1), #black(l2, x2, y2, r2)) { - switch (append(r1, l2)) { - case (#red(l3, x3, y3, r3)) { - #red( - #black(l1, x1, y1, l3), - x3, - y3, - #black(r3, x2, y2, r2) - ) - }; - case r1l2 { - balLeft( - l1, - x1, - y1, - #black(r1l2, x2, y2, r2) - ) - } - } - } - } - }; - - public func delete(m : Tree, compare : (K, K) -> Order.Order, key : K) : Tree = remove(m, compare, key).0; - - public func remove(tree : Tree, compare : (K, K) -> Order.Order, x : K) : (Tree, ?V) { - var y0 : ?V = null; - func delNode(left : Tree, x1 : K, y1 : V, right : Tree) : Tree { - switch (compare(x, x1)) { - case (#less) { - let newLeft = del left; - switch left { - case (#black(_, _, _, _)) { - balLeft(newLeft, x1, y1, right) - }; - case _ { - #red(newLeft, x1, y1, right) - } - } - }; - case (#greater) { - let newRight = del right; - switch right { - case (#black(_, _, _, _)) { - balRight(left, x1, y1, newRight) - }; - case _ { - #red(left, x1, y1, newRight) - } - } - }; - case (#equal) { - y0 := ?y1; - append(left, right) - } - } - }; - func del(tree : Tree) : Tree { - switch tree { - case (#red(left, x, y, right)) { - delNode(left, x, y, right) - }; - case (#black(left, x, y, right)) { - delNode(left, x, y, right) - }; - case (#leaf) { - tree - } - } - }; - switch (del(tree)) { - case (#red(left, x, y, right)) { (#black(left, x, y, right), y0) }; - case other { (other, y0) } - } - }; - - // Test helper - public func validate(rbMap : Map, comp : (K, K) -> Order.Order) { - ignore blackDepth(rbMap.root, comp) - }; - - func blackDepth(node : Tree, comp : (K, K) -> Order.Order) : Nat { - func checkNode(left : Tree, key : K, right : Tree) : Nat { - checkKey(left, func(x : K) : Bool { comp(x, key) == #less }); - checkKey(right, func(x : K) : Bool { comp(x, key) == #greater }); - let leftBlacks = blackDepth(left, comp); - let rightBlacks = blackDepth(right, comp); - assert (leftBlacks == rightBlacks); - leftBlacks - }; - switch node { - case (#leaf) 0; - case (#red(left, key, _, right)) { - let leftBlacks = checkNode(left, key, right); - assert (not isRed(left)); - assert (not isRed(right)); - leftBlacks - }; - case (#black(left, key, _, right)) { - checkNode(left, key, right) + 1 - } - } - }; - - func isRed(node : Tree) : Bool { - switch node { - case (#red(_, _, _, _)) true; - case _ false - } - }; - - func checkKey(node : Tree, isValid : K -> Bool) { - switch node { - case (#leaf) {}; - case (#red(_, key, _, _)) { - assert (isValid(key)) - }; - case (#black(_, key, _, _)) { - assert (isValid(key)) - } - } - } - }; - -} diff --git a/.mops/core@2.4.0/src/pure/Queue.mo b/.mops/core@2.4.0/src/pure/Queue.mo deleted file mode 100644 index e179de0..0000000 --- a/.mops/core@2.4.0/src/pure/Queue.mo +++ /dev/null @@ -1,659 +0,0 @@ -/// Double-ended queue of a generic element type `T`. -/// -/// The interface is purely functional, not imperative, and queues are immutable values. -/// In particular, Queue operations such as push and pop do not update their input queue but, instead, return the -/// value of the modified Queue, alongside any other data. -/// The input queue is left unchanged. -/// -/// Examples of use-cases: -/// Queue (FIFO) by using `pushBack()` and `popFront()`. -/// Stack (LIFO) by using `pushFront()` and `popFront()`. -/// -/// A Queue is internally implemented as two lists, a head access list and a (reversed) tail access list, -/// that are dynamically size-balanced by splitting. -/// -/// Construction: Create a new queue with the `empty()` function. -/// -/// Note on the costs of push and pop functions: -/// * Runtime: `O(1)` amortized costs, `O(size)` worst case cost per single call. -/// * Space: `O(1)` amortized costs, `O(size)` worst case cost per single call. -/// -/// `n` denotes the number of elements stored in the queue. -/// -/// Note that some operations that traverse the elements of the queue (e.g. `forEach`, `values`) preserve the order of the elements, -/// whereas others (e.g. `map`, `contains`) do NOT guarantee that the elements are visited in any order. -/// The order is undefined to avoid allocations, making these operations more efficient. -/// -/// ```motoko name=import -/// import Queue "mo:core/pure/Queue"; -/// ``` - -import Iter "../Iter"; -import List "List"; -import Order "../Order"; -import Types "../Types"; -import Array "../Array"; -import Prim "mo:⛔"; - -module { - /// @deprecated M0235 - type List = Types.Pure.List; - - /// Double-ended queue data type. - public type Queue = Types.Pure.Queue; - - /// Create a new empty queue. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func empty() : Queue = (null, 0, null); - - /// Determine whether a queue is empty. - /// Returns true if `queue` is empty, otherwise `false`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func isEmpty(self : Queue) : Bool = self.1 == 0; - - /// Create a new queue comprising a single element. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.singleton(25); - /// assert Queue.size(queue) == 1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func singleton(item : T) : Queue = (null, 1, ?(item, null)); - - /// Determine the number of elements contained in a queue. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.singleton(42); - /// assert Queue.size(queue) == 1; - /// } - /// ``` - /// - /// Runtime: `O(1)` in Release profile (compiled with `--release` flag), `O(size)` otherwise. - /// - /// Space: `O(1)`. - public func size(self : Queue) : Nat { - debug assert self.1 == List.size(self.0) + List.size(self.2); - self.1 - }; - - /// Check if a queue contains a specific element. - /// Returns true if the queue contains an element equal to `item` according to the `equal` function. - /// - /// Note: The order in which elements are visited is undefined, for performance reasons. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.contains(queue, Nat.equal, 2); - /// assert not Queue.contains(queue, Nat.equal, 4); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func contains(self : Queue, equal : (implicit : (T, T) -> Bool), item : T) : Bool = List.contains(self.0, equal, item) or List.contains(self.2, equal, item); - - /// Inspect the optional element on the front end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, the front element of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekFront(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func peekFront(self : Queue) : ?T = switch self { - case ((?(x, _), _, _) or (_, _, ?(x, null))) ?x; - case _ { debug assert List.isEmpty(self.2); null } - }; - - /// Inspect the optional element on the back end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, the back element of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// assert Queue.peekBack(queue) == ?2; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func peekBack(self : Queue) : ?T = switch self { - case ((_, _, ?(x, _)) or (?(x, null), _, _)) ?x; - case _ { debug assert List.isEmpty(self.0); null } - }; - - // helper to rebalance the queue after getting lopsided - func check(q : Queue) : Queue { - switch q { - case (null, n, r) { - let (a, b) = List.split(r, n / 2); - (List.reverse b, n, a) - }; - case (f, n, null) { - let (a, b) = List.split(f, n / 2); - (a, n, List.reverse b) - }; - case q q - } - }; - - /// Insert a new element on the front end of a queue. - /// Returns the new queue with `element` in the front followed by the elements of `queue`. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekFront(queue) == ?1; - /// assert Queue.peekBack(queue) == ?2; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: `O(size)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(size)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the queue. - public func pushFront(self : Queue, element : T) : Queue = check(?(element, self.0), self.1 + 1, self.2); - - /// Insert a new element on the back end of a queue. - /// Returns the new queue with all the elements of `queue`, followed by `element` on the back. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// assert Queue.peekBack(queue) == ?2; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: `O(size)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(size)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the queue. - public func pushBack(self : Queue, element : T) : Queue = check(self.0, self.1 + 1, ?(element, self.2)); - - /// Remove the element on the front end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, it returns a pair of - /// the first element and a new queue that contains all the remaining elements of `queue`. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko include=import - /// import Runtime "mo:core/Runtime"; - /// - /// persistent actor { - /// let initial = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// // initial queue with elements [1, 2] - /// switch (Queue.popFront(initial)) { - /// case null Runtime.trap "Empty queue impossible"; - /// case (?(frontElement, remainingQueue)) { - /// assert frontElement == 1; - /// assert Queue.size(remainingQueue) == 1 - /// } - /// } - /// } - /// ``` - /// - /// Runtime: `O(size)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(size)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the queue. - public func popFront(self : Queue) : ?(T, Queue) = if (self.1 == 0) null else switch self { - case (?(i, f), n, b) ?(i, (f, n - 1, b)); - case (null, _, ?(i, null)) ?(i, (null, 0, null)); - case _ popFront(check self) - }; - - /// Remove the element on the back end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, it returns a pair of - /// a new queue that contains the remaining elements of `queue` - /// and, as the second pair item, the removed back element. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko include=import - /// import Runtime "mo:core/Runtime"; - /// - /// persistent actor { - /// let initial = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// // initial queue with elements [1, 2] - /// let reduced = Queue.popBack(initial); - /// switch reduced { - /// case null Runtime.trap("Empty queue impossible"); - /// case (?result) { - /// let reducedQueue = result.0; - /// let removedElement = result.1; - /// assert removedElement == 2; - /// assert Queue.size(reducedQueue) == 1; - /// } - /// } - /// } - /// ``` - /// - /// Runtime: `O(size)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(size)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the queue. - public func popBack(self : Queue) : ?(Queue, T) = if (self.1 == 0) null else switch self { - case (f, n, ?(i, b)) ?((f, n - 1, b), i); - case (?(i, null), _, null) ?((null, 0, null), i); - case _ popBack(check self) - }; - - /// Turn an iterator into a queue, consuming it. - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([0, 1, 2, 3, 4].values()); - /// assert Queue.size(queue) == 5; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromIter(iter : Iter.Iter) : Queue { - let list = List.fromIter iter; - check(list, List.size list, null) - }; - - /// Convert an iterator to a queue, consuming it. - /// Example: - /// ```motoko include=import - /// persistent actor { - /// transient let iter = [0, 1, 2, 3, 4].values(); - /// - /// let queue = iter.toQueue(); - /// assert Queue.size(queue) == 5; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toQueue(self : Iter.Iter) : Queue { - fromIter(self) - }; - - /// Create a queue from an array. - /// Elements appear in the same order as in the array. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromArray(["A", "B", "C"]); - /// assert Queue.size(queue) == 3; - /// assert Queue.peekFront(queue) == ?"A"; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromArray(array : [T]) : Queue { - let list = List.fromArray array; - check(list, array.size(), null) - }; - - /// Create an immutable array from a queue. - /// Elements appear in the same order as in the queue (front to back). - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// - /// persistent actor { - /// let queue = Queue.fromArray(["A", "B", "C"]); - /// let array = Queue.toArray(queue); - /// assert array == ["A", "B", "C"]; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toArray(self : Queue) : [T] { - let iter = values(self); - Array.tabulate( - self.1, - func(i) { - switch (iter.next()) { - case null { - Prim.trap("pure/Queue.toArray: unexpected end of iterator") - }; - case (?value) { value } - } - } - ) - }; - - /// Convert a queue to an iterator of its elements in front-to-back order. - /// - /// Performance note: Creating the iterator needs `O(size)` runtime and space! - /// - /// Example: - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Iter.toArray(Queue.values(queue)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func values(self : Queue) : Iter.Iter = Iter.concat(List.values(self.0), List.values(List.reverse(self.2))); - - /// Compare two queues for equality using the provided equality function. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2].values()); - /// let queue2 = Queue.fromIter([1, 2].values()); - /// let queue3 = Queue.fromIter([1, 3].values()); - /// assert Queue.equal(queue1, queue2, Nat.equal); - /// assert not Queue.equal(queue1, queue3, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func equal(self : Queue, other : Queue, equal : (implicit : (T, T) -> Bool)) : Bool { - if (self.1 != other.1) { - return false - }; - let (iter1, iter2) = (values(self), values(other)); - loop { - switch (iter1.next(), iter2.next()) { - case (null, null) { return true }; - case (?v1, ?v2) { - if (not equal(v1, v2)) { return false } - }; - case (_, _) { return false } - } - } - }; - - /// Return true if the given predicate `f` is true for all queue - /// elements. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let allGreaterThanOne = Queue.all(queue, func n = n > 1); - /// assert not allGreaterThanOne; // false because 1 is not > 1 - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` as the current implementation uses `values` to iterate over the queue. - /// - /// *Runtime and space assumes that the `predicate` runs in `O(1)` time and space. - public func all(self : Queue, predicate : T -> Bool) : Bool { - for (item in values self) if (not (predicate item)) return false; - return true - }; - - /// Return true if there exists a queue element for which - /// the given predicate `f` is true. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let hasGreaterThanOne = Queue.any(queue, func n = n > 1); - /// assert hasGreaterThanOne; // true because 2 and 3 are > 1 - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` as the current implementation uses `values` to iterate over the queue. - /// - /// *Runtime and space assumes that the `predicate` runs in `O(1)` time and space. - public func any(self : Queue, predicate : T -> Bool) : Bool { - for (item in values self) if (predicate item) return true; - return false - }; - - /// Call the given function for its side effect, with each queue element in turn. - /// The order of visiting elements is front-to-back. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// var text = ""; - /// let queue = Queue.fromIter(["A", "B", "C"].values()); - /// Queue.forEach(queue, func n = text #= n); - /// assert text == "ABC"; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func forEach(self : Queue, f : T -> ()) = for (item in values self) f item; - - /// Call the given function `f` on each queue element and collect the results - /// in a new queue. - /// - /// Note: The order of visiting elements is undefined with the current implementation. - /// - /// Example: - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([0, 1, 2].values()); - /// let textQueue = Queue.map(queue, Nat.toText); - /// assert Iter.toArray(Queue.values(textQueue)) == ["0", "1", "2"]; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func map(self : Queue, f : T1 -> T2) : Queue { - let (fr, n, b) = self; - (List.map(fr, f), n, List.map(b, f)) - }; - - /// Create a new queue with only those elements of the original queue for which - /// the given function (often called the _predicate_) returns true. - /// - /// Note: The order of visiting elements is undefined with the current implementation. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([0, 1, 2, 1].values()); - /// let filtered = Queue.filter(queue, func n = n != 1); - /// assert Queue.size(filtered) == 2; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func filter(self : Queue, predicate : T -> Bool) : Queue { - let (fr, _, b) = self; - let front = List.filter(fr, predicate); - let back = List.filter(b, predicate); - check(front, List.size front + List.size back, back) - }; - - /// Call the given function on each queue element, and collect the non-null results - /// in a new queue. - /// - /// Note: The order of visiting elements is undefined with the current implementation. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let doubled = Queue.filterMap( - /// queue, - /// func n = if (n > 1) ?(n * 2) else null - /// ); - /// assert Queue.size(doubled) == 2; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func filterMap(self : Queue, f : T -> ?U) : Queue { - let (fr, _n, b) = self; - let front = List.filterMap(fr, f); - let back = List.filterMap(b, f); - check(front, List.size front + List.size back, back) - }; - - /// Convert a queue to its text representation using the provided conversion function. - /// This function is meant to be used for debugging and testing purposes. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.toText(queue, Nat.toText) == "PureQueue[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func toText(self : Queue, f : (implicit : (toText : T -> Text))) : Text { - var text = "PureQueue["; - func add(item : T) { - if (text.size() > 10) text #= ", "; - text #= f(item) - }; - List.forEach(self.0, add); - List.forEach(List.reverse(self.2), add); - text # "]" - }; - - /// Compare two queues using lexicographic ordering specified by argument function `compareItem`. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2].values()); - /// let queue2 = Queue.fromIter([1, 3].values()); - /// assert Queue.compare(queue1, queue2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that argument `compareItem` runs in `O(1)` time and space. - public func compare(self : Queue, other : Queue, compareItem : (implicit : (compare : (T, T) -> Order.Order))) : Order.Order { - let (i1, i2) = (values self, values other); - loop switch (i1.next(), i2.next()) { - case (?v1, ?v2) switch (compareItem(v1, v2)) { - case (#equal) (); - case c return c - }; - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater - } - }; - - /// Reverse the order of elements in a queue. - /// This operation is cheap, it does NOT require copying the elements. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let reversed = Queue.reverse(queue); - /// assert Queue.peekFront(reversed) == ?3; - /// assert Queue.peekBack(reversed) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func reverse(self : Queue) : Queue = (self.2, self.1, self.0) -} diff --git a/.mops/core@2.4.0/src/pure/RealTimeQueue.mo b/.mops/core@2.4.0/src/pure/RealTimeQueue.mo deleted file mode 100644 index adeb25f..0000000 --- a/.mops/core@2.4.0/src/pure/RealTimeQueue.mo +++ /dev/null @@ -1,1175 +0,0 @@ -/// Double-ended immutable queue with guaranteed `O(1)` push/pop operations (caveat: high constant factor). -/// For a default immutable queue implementation, see `pure/Queue`. -/// -/// This module provides an alternative implementation with better worst-case performance for single operations, e.g. `pushBack` and `popFront`. -/// These operations are always constant time, `O(1)`, which eliminates spikes in performance of `pure/Queue` operations -/// that are caused by the amortized nature of the `pure/Queue` implementation, which can lead to `O(n)` worst-case performance for a single operation. -/// The spikes in performance can cause a single message to take multiple more rounds to complete than most other messages. -/// -/// However, the `O(1)` operations come at a cost of higher constant factor than the `pure/Queue` implementation: -/// - 'pop' operations are on average 3x more expensive -/// - 'push' operations are on average 8x more expensive -/// -/// For better performance across multiple operations and when the spikes in single operations are not a problem, use `pure/Queue`. -/// For guaranteed `O(1)` operations, use `pure/RealTimeQueue`. -/// -/// --- -/// -/// The interface is purely functional, not imperative, and queues are immutable values. -/// In particular, Queue operations such as push and pop do not update their input queue but, instead, return the -/// value of the modified Queue, alongside any other data. -/// The input queue is left unchanged. -/// -/// Examples of use-cases: -/// - Queue (FIFO) by using `pushBack()` and `popFront()`. -/// - Stack (LIFO) by using `pushFront()` and `popFront()`. -/// - Deque (double-ended queue) by using any combination of push/pop operations on either end. -/// -/// A Queue is internally implemented as a real-time double-ended queue based on the paper -/// "Real-Time Double-Ended Queue Verified (Proof Pearl)". The implementation maintains -/// worst-case constant time `O(1)` for push/pop operations through gradual rebalancing steps. -/// -/// Construction: Create a new queue with the `empty()` function. -/// -/// Note that some operations that traverse the elements of the queue (e.g. `forEach`, `values`) preserve the order of the elements, -/// whereas others (e.g. `map`, `contains`) do NOT guarantee that the elements are visited in any order. -/// The order is undefined to avoid allocations, making these operations more efficient. -/// -/// ```motoko name=import -/// import Queue "mo:core/pure/RealTimeQueue"; -/// ``` - -import Types "../Types"; -import List "List"; -import Option "../Option"; -import { trap } "../Runtime"; -import Iter "../Iter"; - -module { - /// The real-time queue data structure can be in one of the following states: - /// - /// - `#empty`: the queue is empty - /// - `#one`: the queue contains a single element - /// - `#two`: the queue contains two elements - /// - `#three`: the queue contains three elements - /// - `#idles`: the queue is in the idle state, where `l` and `r` are non-empty stacks of elements fulfilling the size invariant - /// - `#rebal`: the queue is in the rebalancing state - public type Queue = { - #empty; - #one : T; - #two : (T, T); - #three : (T, T, T); - #idles : (Idle, Idle); - #rebal : States - }; - - /// Create a new empty queue. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func empty() : Queue = #empty; - - /// Determine whether a queue is empty. - /// Returns true if `queue` is empty, otherwise `false`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func isEmpty(self : Queue) : Bool = switch self { - case (#empty) true; - case _ false - }; - - /// Create a new queue comprising a single element. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.singleton(25); - /// assert Queue.size(queue) == 1; - /// assert Queue.peekFront(queue) == ?25; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func singleton(element : T) : Queue = #one(element); - - /// Determine the number of elements contained in a queue. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.singleton(42); - /// assert Queue.size(queue) == 1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func size(self : Queue) : Nat = switch self { - case (#empty) 0; - case (#one _) 1; - case (#two _) 2; - case (#three _) 3; - case (#idles((l, nL), (r, nR))) { - debug assert Stacks.size(l) == nL and Stacks.size(r) == nR; - nL + nR - }; - case (#rebal(_, big, small)) BigState.size(big) + SmallState.size(small) - }; - - /// Test if a queue contains a given value. - /// Returns true if the queue contains the item, otherwise false. - /// - /// Note: The order in which elements are visited is undefined, for performance reasons. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// assert Queue.contains(queue, Nat.equal, 1); - /// assert not Queue.contains(queue, Nat.equal, 3); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func contains(self : Queue, equal : (implicit : (T, T) -> Bool), item : T) : Bool = switch self { - case (#empty) false; - case (#one(x)) equal(x, item); - case (#two(x, y)) equal(x, item) or equal(y, item); - case (#three(x, y, z)) equal(x, item) or equal(y, item) or equal(z, item); - case (#idles(((l1, l2), _), ((r1, r2), _))) List.contains(l1, equal, item) or List.contains(l2, equal, item) or List.contains(r2, equal, item) or List.contains(r1, equal, item); // note that the order of the right stack is reversed, but for this operation it does not matter - case (#rebal(_, big, small)) { - let (extraB, _, (oldB1, oldB2), _) = BigState.current(big); - let (extraS, _, (oldS1, oldS2), _) = SmallState.current(small); - // note that the order of one of the stacks is reversed (depending on the `direction` field), but for this operation it does not matter - List.contains(extraB, equal, item) or List.contains(oldB1, equal, item) or List.contains(oldB2, equal, item) or List.contains(extraS, equal, item) or List.contains(oldS1, equal, item) or List.contains(oldS2, equal, item) - } - }; - - /// Inspect the optional element on the front end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, the front element of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekFront(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func peekFront(self : Queue) : ?T = switch self { - case (#idles((l, _), _)) Stacks.first(l); - case (#rebal(dir, big, small)) switch dir { - case (#left) ?SmallState.peek(small); - case (#right) ?BigState.peek(big) - }; - case (#empty) null; - case (#one(x)) ?x; - case (#two(x, _)) ?x; - case (#three(x, _, _)) ?x - }; - - /// Inspect the optional element on the back end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, the back element of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekBack(queue) == ?2; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func peekBack(self : Queue) : ?T = switch self { - case (#idles(_, (r, _))) Stacks.first(r); - case (#rebal(dir, big, small)) switch dir { - case (#left) ?BigState.peek(big); - case (#right) ?SmallState.peek(small) - }; - case (#empty) null; - case (#one(x)) ?x; - case (#two(_, y)) ?y; - case (#three(_, _, z)) ?z - }; - - /// Insert a new element on the front end of a queue. - /// Returns the new queue with `element` in the front followed by the elements of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekFront(queue) == ?1; - /// assert Queue.peekBack(queue) == ?2; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: `O(1)` worst-case! - /// - /// Space: `O(1)` worst-case! - public func pushFront(self : Queue, element : T) : Queue = switch self { - case (#idles(l0, rnR)) { - let lnL = Idle.push(l0, element); // enque the element to the left end - // check if the size invariant still holds - if (3 * rnR.1 >= lnL.1) { - debug assert 3 * lnL.1 >= rnR.1; - #idles(lnL, rnR) - } else { - // initiate the rebalancing process - let (l, nL) = lnL; - let (r, nR) = rnR; - let targetSizeL = nL - nR - 1 : Nat; - let targetSizeR = 2 * nR + 1; - debug assert targetSizeL + targetSizeR == nL + nR; - let big = #big1(Current.new(l, targetSizeL), l, null, targetSizeL); - let small = #small1(Current.new(r, targetSizeR), r, null); - let states = (#right, big, small); - let states6 = States.step(States.step(States.step(States.step(States.step(States.step(states)))))); - #rebal(states6) - } - }; - // if the queue is in the middle of a rebalancing process: push the element and advance the rebalancing process by 4 steps - // move back into the idle state if the rebalancing is done - case (#rebal(dir, big0, small0)) switch dir { - case (#right) { - let big = BigState.push(big0, element); - let states4 = States.step(States.step(States.step(States.step((#right, big, small0))))); - debug assert states4.0 == #right; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(big, small); - #idles(big, small) - }; - case _ #rebal(states4) - } - }; - case (#left) { - let small = SmallState.push(small0, element); - let states4 = States.step(States.step(States.step(States.step((#left, big0, small))))); - debug assert states4.0 == #left; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(small, big); - #idles(small, big) // swapped because dir=left - }; - case _ #rebal(states4) - } - } - }; - case (#empty) #one(element); - case (#one(y)) #two(element, y); - case (#two(y, z)) #three(element, y, z); - case (#three(a, b, c)) { - let i1 = ((?(element, ?(a, null)), null), 2); - let i2 = ((?(c, ?(b, null)), null), 2); - #idles(i1, i2) - } - }; - - /// Insert a new element on the back end of a queue. - /// Returns the new queue with all the elements of `queue`, followed by `element` on the back. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// assert Queue.peekBack(queue) == ?2; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: `O(1)` worst-case! - /// - /// Space: `O(1)` worst-case! - public func pushBack(self : Queue, element : T) : Queue = switch self { - // Equivalent to: `reverse(pushFront(reverse(queue), element))`. Inlined for performance. - case (#idles(rnR, l0)) { - // ^ reversed input - let lnL = Idle.push(l0, element); - if (3 * rnR.1 >= lnL.1) { - debug assert 3 * lnL.1 >= rnR.1; - #idles(rnR, lnL) // reversed output - } else { - let (l, nL) = lnL; - let (r, nR) = rnR; - let targetSizeL = nL - nR - 1 : Nat; - let targetSizeR = 2 * nR + 1; - debug assert targetSizeL + targetSizeR == nL + nR; - let big = #big1(Current.new(l, targetSizeL), l, null, targetSizeL); - let small = #small1(Current.new(r, targetSizeR), r, null); - let states = (#left, big, small); // reversed output - let states6 = States.step(States.step(States.step(States.step(States.step(States.step(states)))))); - #rebal(states6) - } - }; - case (#rebal(dir, big0, small0)) switch dir { - case (#left) { - // ^ reversed input - let big = BigState.push(big0, element); - let states4 = States.step(States.step(States.step(States.step((#left, big, small0))))); // reversed output - debug assert states4.0 == #left; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(big, small); - #idles(small, big) // reversed output - }; - case _ #rebal(states4) - } - }; - case (#right) { - // ^ reversed input - let small = SmallState.push(small0, element); - let states4 = States.step(States.step(States.step(States.step((#right, big0, small))))); // reversed output - debug assert states4.0 == #right; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(small, big); - #idles(big, small) // reversed output - }; - case _ #rebal(states4) - } - } - }; - case (#empty) #one(element); - case (#one(y)) #two(y, element); - case (#two(y, z)) #three(y, z, element); - case (#three(a, b, c)) { - let i1 = ((?(a, ?(b, null)), null), 2); - let i2 = ((?(element, ?(c, null)), null), 2); - #idles(i1, i2) - } - }; - - /// Remove the element on the front end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, it returns a pair of - /// the first element and a new queue that contains all the remaining elements of `queue`. - /// - /// Example: - /// ```motoko include=import - /// import Runtime "mo:core/Runtime"; - /// - /// persistent actor { - /// do { - /// let initial = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// let ?(frontElement, remainingQueue) = Queue.popFront(initial) else Runtime.trap "Empty queue impossible"; - /// assert frontElement == 1; - /// assert Queue.size(remainingQueue) == 1; - /// } - /// } - /// ``` - /// - /// Runtime: `O(1)` worst-case! - /// - /// Space: `O(1)` worst-case! - public func popFront(self : Queue) : ?(T, Queue) = switch self { - case (#idles(l0, rnR)) { - let (x, lnL) = Idle.pop(l0); - if (3 * lnL.1 >= rnR.1) { - ?(x, #idles(lnL, rnR)) - } else if (lnL.1 >= 1) { - let (l, nL) = lnL; - let (r, nR) = rnR; - let targetSizeL = 2 * nL + 1; - let targetSizeR = nR - nL - 1 : Nat; - debug assert targetSizeL + targetSizeR == nL + nR; - let small = #small1(Current.new(l, targetSizeL), l, null); - let big = #big1(Current.new(r, targetSizeR), r, null, targetSizeR); - let states = (#left, big, small); - let states6 = States.step(States.step(States.step(States.step(States.step(States.step(states)))))); - ?(x, #rebal(states6)) - } else { - ?(x, Stacks.smallqueue(rnR.0)) - } - }; - case (#rebal(dir, big0, small0)) switch dir { - case (#left) { - let (x, small) = SmallState.pop(small0); - let states4 = States.step(States.step(States.step(States.step((#left, big0, small))))); - debug assert states4.0 == #left; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(small, big); - ?(x, #idles(small, big)) - }; - case _ ?(x, #rebal(states4)) - } - }; - case (#right) { - let (x, big) = BigState.pop(big0); - let states4 = States.step(States.step(States.step(States.step((#right, big, small0))))); - debug assert states4.0 == #right; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(big, small); - ?(x, #idles(big, small)) - }; - case _ ?(x, #rebal(states4)) - } - } - }; - case (#empty) null; - case (#one(x)) ?(x, #empty); - case (#two(x, y)) ?(x, #one(y)); - case (#three(x, y, z)) ?(x, #two(y, z)) - }; - - /// Remove the element on the back end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, it returns a pair of - /// a new queue that contains the remaining elements of `queue` - /// and, as the second pair item, the removed back element. - /// - /// Example: - /// ```motoko include=import - /// import Runtime "mo:core/Runtime"; - /// - /// persistent actor { - /// do { - /// let initial = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// let ?(reducedQueue, removedElement) = Queue.popBack(initial) else Runtime.trap "Empty queue impossible"; - /// assert removedElement == 2; - /// assert Queue.size(reducedQueue) == 1; - /// } - /// } - /// ``` - /// - /// Runtime: `O(1)` worst-case! - /// - /// Space: `O(1)` worst-case! - public func popBack(self : Queue) : ?(Queue, T) = switch self { - // Equivalent to: - // = do ? { let (x, queue2) = popFront(reverse(queue))!; (reverse(queue2), x) }; - // Inlined for performance. - case (#idles(rnR, l0)) { - // ^ reversed input - let (x, lnL) = Idle.pop(l0); - if (3 * lnL.1 >= rnR.1) { - ?(#idles(rnR, lnL), x) // reversed output - } else if (lnL.1 >= 1) { - let (l, nL) = lnL; - let (r, nR) = rnR; - let targetSizeL = 2 * nL + 1; - let targetSizeR = nR - nL - 1 : Nat; - debug assert targetSizeL + targetSizeR == nL + nR; - let small = #small1(Current.new(l, targetSizeL), l, null); - let big = #big1(Current.new(r, targetSizeR), r, null, targetSizeR); - let states = (#right, big, small); // reversed output - let states6 = States.step(States.step(States.step(States.step(States.step(States.step(states)))))); - ?(#rebal(states6), x) - } else { - ?(Stacks.smallqueueReversed(rnR.0), x) // reversed output - } - }; - case (#rebal(dir, big0, small0)) switch dir { - case (#right) { - // ^ reversed input - let (x, small) = SmallState.pop(small0); - let states4 = States.step(States.step(States.step(States.step((#right, big0, small))))); // reversed output - debug assert states4.0 == #right; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(big, small); - ?(#idles(big, small), x) // reversed output - }; - case _ ?(#rebal(states4), x) - } - }; - case (#left) { - // ^ reversed input - let (x, big) = BigState.pop(big0); - let states4 = States.step(States.step(States.step(States.step((#left, big, small0))))); // reversed output - debug assert states4.0 == #left; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(small, big); - ?(#idles(small, big), x) // reversed output - }; - case _ ?(#rebal(states4), x) - } - } - }; - case (#empty) null; - case (#one(x)) ?(#empty, x); - case (#two(x, y)) ?(#one(x), y); - case (#three(x, y, z)) ?(#two(x, y), z) - }; - - /// Turn an iterator into a queue, consuming it. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([0, 1, 2, 3, 4].values()); - /// assert Queue.peekFront(queue) == ?0; - /// assert Queue.peekBack(queue) == ?4; - /// assert Queue.size(queue) == 5; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func fromIter(iter : Iter) : Queue { - var queue = empty(); - Iter.forEach(iter, func(t : T) = queue := pushBack(queue, t)); - queue - }; - - /// Convert an iterator into a queue, consuming the iterator. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// transient let iter = [0, 1, 2, 3, 4].values(); - /// - /// let queue = iter.toQueue(); - /// - /// assert Queue.peekFront(queue) == ?0; - /// assert Queue.peekBack(queue) == ?4; - /// assert Queue.size(queue) == 5; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func toQueue(self : Iter) : Queue { - fromIter(self) - }; - - /// Create an iterator over the elements in the queue. The order of the elements is from front to back. - /// - /// Example: - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Iter.toArray(Queue.values(queue)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(1)` to create the iterator and for each `next()` call. - /// - /// Space: `O(1)` to create the iterator and for each `next()` call. - public func values(self : Queue) : Iter.Iter { - object { - var current = self; - public func next() : ?T { - switch (popFront(current)) { - case null null; - case (?result) { - current := result.1; - ?result.0 - } - } - } - } - }; - - /// Compare two queues for equality using a provided equality function to compare their elements. - /// Two queues are considered equal if they contain the same elements in the same order. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2, 3].values()); - /// let queue2 = Queue.fromIter([1, 2, 3].values()); - /// let queue3 = Queue.fromIter([1, 3, 2].values()); - /// assert Queue.equal(queue1, queue2, Nat.equal); - /// assert not Queue.equal(queue1, queue3, Nat.equal); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func equal(self : Queue, other : Queue, equal : (implicit : (T, T) -> Bool)) : Bool { - if (size(self) != size(other)) { - return false - }; - func go(self : Queue, other : Queue, equal : (T, T) -> Bool) : Bool = switch (popFront self, popFront other) { - case (null, null) true; - case (?(x1, tail1), ?(x2, tail2)) equal(x1, x2) and go(tail1, tail2, equal); // Note that this is tail recursive (`and` is expanded to `if`). - case _ false - }; - go(self, other, equal) - }; - - /// Compare two queues lexicographically using a provided comparison function to compare their elements. - /// Returns `#less` if `queue1` is lexicographically less than `queue2`, `#equal` if they are equal, and `#greater` otherwise. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2, 3].values()); - /// let queue2 = Queue.fromIter([1, 2, 4].values()); - /// assert Queue.compare(queue1, queue2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func compare(self : Queue, other : Queue, compareItem : (implicit : (compare : (T, T) -> Types.Order))) : Types.Order = switch (popFront self, popFront other) { - case (null, null) #equal; - case (null, _) #less; - case (_, null) #greater; - case (?(x1, selfTail), ?(x2, otherTail)) { - switch (compareItem(x1, x2)) { - case (#equal) compare(selfTail, otherTail, compareItem); - case order order - } - } - }; - - /// Return true if the given predicate is true for all queue elements. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([2, 4, 6].values()); - /// assert Queue.all(queue, func n = n % 2 == 0); - /// assert not Queue.all(queue, func n = n > 4); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` as the current implementation uses `values` to iterate over the queue. - /// - /// *Runtime and space assumes that the `predicate` runs in `O(1)` time and space. - public func all(self : Queue, predicate : T -> Bool) : Bool = switch self { - case (#empty) true; - case (#one(x)) predicate x; - case (#two(x, y)) predicate x and predicate y; - case (#three(x, y, z)) predicate x and predicate y and predicate z; - case _ { - for (item in values self) if (not (predicate item)) return false; - return true - } - }; - - /// Return true if the given predicate is true for any queue element. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.any(queue, func n = n > 2); - /// assert not Queue.any(queue, func n = n > 3); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` as the current implementation uses `values` to iterate over the queue. - /// - /// *Runtime and space assumes that the `predicate` runs in `O(1)` time and space. - public func any(self : Queue, predicate : T -> Bool) : Bool = switch self { - case (#empty) false; - case (#one(x)) predicate x; - case (#two(x, y)) predicate x or predicate y; - case (#three(x, y, z)) predicate x or predicate y or predicate z; - case _ { - for (item in values self) if (predicate item) return true; - return false - } - }; - - /// Call the given function for its side effect on each queue element in order: from front to back. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// persistent actor { - /// var text = ""; - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// Queue.forEach(queue, func n = text #= Nat.toText(n)); - /// assert text == "123"; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func forEach(self : Queue, f : T -> ()) = switch self { - case (#empty) (); - case (#one(x)) f x; - case (#two(x, y)) { f x; f y }; - case (#three(x, y, z)) { f x; f y; f z }; - // Preserve the order when visiting the elements. Note that the #idles case would require reversing the second stack. - case _ { - for (t in values self) f t - } - }; - - /// Create a new queue by applying the given function to each element of the original queue. - /// - /// Note: The order of visiting elements is undefined with the current implementation. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let mapped = Queue.map(queue, func n = n * 2); - /// assert Queue.size(mapped) == 3; - /// assert Queue.peekFront(mapped) == ?2; - /// assert Queue.peekBack(mapped) == ?6; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func map(self : Queue, f : T1 -> T2) : Queue = switch self { - case (#empty) #empty; - case (#one(x)) #one(f x); - case (#two(x, y)) #two(f x, f y); - case (#three(x, y, z)) #three(f x, f y, f z); - case (#idles(l, r)) #idles(Idle.map(l, f), Idle.map(r, f)); - case (#rebal(_)) { - // No reason to rebuild the #rebal state. - // future work: It could be further optimized by building a balanced #idles state directly since we know the sizes. - var q = empty(); - for (t in values self) q := pushBack(q, f t); - q - } - }; - - /// Create a new queue with only those elements of the original queue for which - /// the given predicate returns true. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3, 4].values()); - /// let filtered = Queue.filter(queue, func n = n % 2 == 0); - /// assert Queue.size(filtered) == 2; - /// assert Queue.peekFront(filtered) == ?2; - /// assert Queue.peekBack(filtered) == ?4; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func filter(self : Queue, predicate : T -> Bool) : Queue { - var q = empty(); - for (t in values self) if (predicate t) q := pushBack(q, t); - q - }; - - /// Create a new queue by applying the given function to each element of the original queue - /// and collecting the results for which the function returns a non-null value. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3, 4].values()); - /// let filtered = Queue.filterMap(queue, func n = if (n % 2 == 0) { ?n } else null); - /// assert Queue.size(filtered) == 2; - /// assert Queue.peekFront(filtered) == ?2; - /// assert Queue.peekBack(filtered) == ?4; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that f runs in `O(1)` time and space. - public func filterMap(self : Queue, f : T -> ?U) : Queue { - var q = empty(); - for (t in values self) { - switch (f t) { - case (?x) q := pushBack(q, x); - case null () - } - }; - q - }; - - /// Create a `Text` representation of a queue for debugging purposes. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.toText(queue, Nat.toText) == "RealTimeQueue[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that f runs in `O(1)` time and space. - public func toText(self : Queue, f : (implicit : (toText : T -> Text))) : Text { - var text = "RealTimeQueue["; - var first = true; - for (t in values self) { - if (first) first := false else text #= ", "; - text #= f(t) - }; - text # "]" - }; - - /// Reverse the order of elements in a queue. - /// This operation is cheap, it does NOT require copying the elements. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let reversed = Queue.reverse(queue); - /// assert Queue.peekFront(reversed) == ?3; - /// assert Queue.peekBack(reversed) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func reverse(self : Queue) : Queue = switch self { - case (#idles(l, r)) #idles(r, l); - case (#rebal(#left, big, small)) #rebal(#right, big, small); - case (#rebal(#right, big, small)) #rebal(#left, big, small); - case (#empty) self; - case (#one(_)) self; - case (#two(x, y)) #two(y, x); - case (#three(x, y, z)) #three(z, y, x) - }; - - type Stacks = (left : List, right : List); - - module Stacks { - public func push((left, right) : Stacks, t : T) : Stacks = (?(t, left), right); - - public func pop(stacks : Stacks) : Stacks = switch stacks { - case (?(_, leftTail), right) (leftTail, right); - case (null, ?(_, rightTail)) (null, rightTail); - case (null, null) stacks - }; - - public func first((left, right) : Stacks) : ?T = switch (left) { - case (?(h, _)) ?h; - case (null) do ? { right!.0 } - }; - - public func unsafeFirst((left, right) : Stacks) : T = switch (left) { - case (?(h, _)) h; - case (null) Option.unwrap(right).0 - }; - - public func isEmpty((left, right) : Stacks) : Bool = List.isEmpty(left) and List.isEmpty(right); - - public func size((left, right) : Stacks) : Nat = List.size(left) + List.size(right); - - public func smallqueue((left, right) : Stacks) : Queue = switch (left, right) { - case (null, null) #empty; - case (null, ?(x, null)) #one(x); - case (?(x, null), null) #one(x); - case (null, ?(x, ?(y, null))) #two(y, x); - case (?(x, null), ?(y, null)) #two(y, x); - case (?(x, ?(y, null)), null) #two(y, x); - case (null, ?(x, ?(y, ?(z, null)))) #three(z, y, x); - case (?(x, ?(y, ?(z, null))), null) #three(z, y, x); - case (?(x, ?(y, null)), ?(z, null)) #three(z, y, x); - case (?(x, null), ?(y, ?(z, null))) #three(z, y, x); - case _ (trap "Queue.Stacks.smallqueue() impossible") - }; - - public func smallqueueReversed((left, right) : Stacks) : Queue = switch (left, right) { - case (null, null) #empty; - case (null, ?(x, null)) #one(x); - case (?(x, null), null) #one(x); - case (null, ?(x, ?(y, null))) #two(x, y); - case (?(x, null), ?(y, null)) #two(x, y); - case (?(x, ?(y, null)), null) #two(x, y); - case (null, ?(x, ?(y, ?(z, null)))) #three(x, y, z); - case (?(x, ?(y, ?(z, null))), null) #three(x, y, z); - case (?(x, ?(y, null)), ?(z, null)) #three(x, y, z); - case (?(x, null), ?(y, ?(z, null))) #three(x, y, z); - case _ (trap "Queue.Stacks.smallqueueReversed() impossible") - }; - public func map((left, right) : Stacks, f : T -> U) : Stacks = (List.map(left, f), List.map(right, f)) - }; - - /// Represents an end of the queue that is not in a rebalancing process. It is a stack and its size. - type Idle = (stacks : Stacks, size : Nat); - module Idle { - public func push((stacks, size) : Idle, t : T) : Idle = (Stacks.push(stacks, t), 1 + size); - public func pop((stacks, size) : Idle) : (T, Idle) = (Stacks.unsafeFirst(stacks), (Stacks.pop(stacks), size - 1 : Nat)); - public func peek((stacks, _) : Idle) : T = Stacks.unsafeFirst(stacks); - - public func map((stacks, size) : Idle, f : T -> U) : Idle = (Stacks.map(stacks, f), size) - }; - - /// Stores information about operations that happen during rebalancing but which have not become part of the old state that is being rebalanced. - /// - /// - `extra`: newly added elements - /// - `extraSize`: size of `extra` - /// - `old`: elements contained before the rebalancing process - /// - `targetSize`: the number of elements which will be contained after the rebalancing is finished - type Current = (extra : List, extraSize : Nat, old : Stacks, targetSize : Nat); - - module Current { - public func new(old : Stacks, targetSize : Nat) : Current = (null, 0, old, targetSize); - - public func push((extra, extraSize, old, targetSize) : Current, t : T) : Current = (?(t, extra), 1 + extraSize, old, targetSize); - - public func pop((extra, extraSize, old, targetSize) : Current) : (T, Current) = switch (extra) { - case (?(h, t)) (h, (t, extraSize - 1 : Nat, old, targetSize)); - case (null) (Stacks.unsafeFirst(old), (null, extraSize, Stacks.pop(old), targetSize - 1 : Nat)) - }; - - public func peek((extra, _, old, _) : Current) : T = switch (extra) { - case (?(h, _)) h; - case (null) Stacks.unsafeFirst(old) - }; - - public func size((_, extraSize, _, targetSize) : Current) : Nat = extraSize + targetSize - }; - - /// The bigger end of the queue during rebalancing. It is used to split the bigger end of the queue into the new big end and a portion to be added to the small end. Can be in one of the following states: - /// - /// - `#big1(cur, big, aux, n)`: Initial state. Using the step function it takes `n`-elements from the `big` stack and puts them to `aux` in reversed order. `#big1(cur, x1 .. xn : bigTail, [], n) ->* #big1(cur, bigTail, xn .. x1, 0)`. The `bigTail` is later given to the `small` end. - /// - `#big2(common)`: Is used to reverse the elements from the previous phase to restore the original order. `common = #copy(cur, xn .. x1, [], 0) ->* #copy(cur, [], x1 .. xn, n)`. - type BigState = { - #big1 : (Current, Stacks, List, Nat); - #big2 : CommonState - }; - - module BigState { - public func push(big : BigState, t : T) : BigState = switch big { - case (#big1(cur, big, aux, n)) #big1(Current.push(cur, t), big, aux, n); - case (#big2(state)) #big2(CommonState.push(state, t)) - }; - - public func pop(big : BigState) : (T, BigState) = switch big { - case (#big1(cur, big, aux, n)) { - let (x, cur2) = Current.pop(cur); - (x, #big1(cur2, big, aux, n)) - }; - case (#big2(state)) { - let (x, state2) = CommonState.pop(state); - (x, #big2(state2)) - } - }; - - public func peek(big : BigState) : T = switch big { - case (#big1(cur, _, _, _)) Current.peek(cur); - case (#big2(state)) CommonState.peek(state) - }; - - public func step(big : BigState) : BigState = switch big { - case (#big1(cur, big, aux, n)) { - if (n == 0) - #big2(CommonState.norm(#copy(cur, aux, null, 0))) else - #big1(cur, Stacks.pop(big), ?(Stacks.unsafeFirst(big), aux), n - 1 : Nat) - }; - case (#big2(state)) #big2(CommonState.step(state)) - }; - - public func size(big : BigState) : Nat = switch big { - case (#big1(cur, _, _, _)) Current.size(cur); - case (#big2(state)) CommonState.size(state) - }; - - public func current(big : BigState) : Current = switch big { - case (#big1(cur, _, _, _)) cur; - case (#big2(state)) CommonState.current(state) - } - }; - - /// The smaller end of the queue during rebalancing. Can be in one of the following states: - /// - /// - `#small1(cur, small, aux)`: Initial state. Using the step function the original elements are reversed. `#small1(cur, s1 .. sn, []) ->* #small1(cur, [], sn .. s1)`, note that `aux` is initially empty, at the end contains the reversed elements from the small stack. - /// - `#small2(cur, aux, big, new, size)`: Using the step function the newly transfered tail from the bigger end is reversed on top of the `new` list. `#small2(cur, sn .. s1, b1 .. bm, [], 0) ->* #small2(cur, sn .. s1, [], bm .. b1, m)`, note that `aux` is the reversed small stack from the previous phase, `new` is initially empty, `size` corresponds to the size of `new`. - /// - `#small3(common)`: Is used to reverse the elements from the two previous phases again to get them again in the original order. `#copy(cur, sn .. s1, bm .. b1, m) ->* #copy(cur, [], s1 .. sn : bm .. b1, n + m)`, note that the correct order of the elements from the big stack is reversed. - type SmallState = { - #small1 : (Current, Stacks, List); - #small2 : (Current, List, Stacks, List, Nat); - #small3 : CommonState - }; - - module SmallState { - public func push(state : SmallState, t : T) : SmallState = switch state { - case (#small1(cur, small, aux)) #small1(Current.push(cur, t), small, aux); - case (#small2(cur, aux, big, new, newN)) #small2(Current.push(cur, t), aux, big, new, newN); - case (#small3(common)) #small3(CommonState.push(common, t)) - }; - - public func pop(state : SmallState) : (T, SmallState) = switch state { - case (#small1(cur0, small, aux)) { - let (t, cur) = Current.pop(cur0); - (t, #small1(cur, small, aux)) - }; - case (#small2(cur0, aux, big, new, newN)) { - let (t, cur) = Current.pop(cur0); - (t, #small2(cur, aux, big, new, newN)) - }; - case (#small3(common0)) { - let (t, common) = CommonState.pop(common0); - (t, #small3(common)) - } - }; - - public func peek(state : SmallState) : T = switch state { - case (#small1(cur, _, _)) Current.peek(cur); - case (#small2(cur, _, _, _, _)) Current.peek(cur); - case (#small3(common)) CommonState.peek(common) - }; - - public func step(state : SmallState) : SmallState = switch state { - case (#small1(cur, small, aux)) { - if (Stacks.isEmpty(small)) state else #small1(cur, Stacks.pop(small), ?(Stacks.unsafeFirst(small), aux)) - }; - case (#small2(cur, aux, big, new, newN)) { - if (Stacks.isEmpty(big)) #small3(CommonState.norm(#copy(cur, aux, new, newN))) else #small2(cur, aux, Stacks.pop(big), ?(Stacks.unsafeFirst(big), new), 1 + newN) - }; - case (#small3(common)) #small3(CommonState.step(common)) - }; - - public func size(state : SmallState) : Nat = switch state { - case (#small1(cur, _, _)) Current.size(cur); - case (#small2(cur, _, _, _, _)) Current.size(cur); - case (#small3(common)) CommonState.size(common) - }; - - public func current(state : SmallState) : Current = switch state { - case (#small1(cur, _, _)) cur; - case (#small2(cur, _, _, _, _)) cur; - case (#small3(common)) CommonState.current(common) - } - }; - - type CopyState = { #copy : (Current, List, List, Nat) }; - - /// Represents the last rebalancing phase of both small and big ends of the queue. It is used to reverse the elements from the previous phases to restore the original order. It can be in one of the following states: - /// - /// - `#copy(cur, aux, new, sizeOfNew)`: Puts the elements from `aux` in reversed order on top of `new`. `#copy(cur, xn .. x1, new, sizeOfNew) ->* #copy(cur, [], x1 .. xn : new, n + sizeOfNew)`. - /// - `#idle(cur, idle)`: The rebalancing process is done and the queue is in the idle state. - type CommonState = CopyState or { #idle : (Current, Idle) }; - - module CommonState { - public func step(common : CommonState) : CommonState = switch common { - case (#copy copy) { - let (cur, aux, new, sizeOfNew) = copy; - let (_, _, _, targetSize) = cur; - norm(if (sizeOfNew < targetSize) #copy(cur, unsafeTail(aux), ?(unsafeHead(aux), new), 1 + sizeOfNew) else #copy copy) - }; - case (#idle _) common - }; - - public func norm(copy : CopyState) : CommonState { - let #copy(cur, _, new, sizeOfNew) = copy; - let (extra, extraSize, _, targetSize) = cur; - debug assert sizeOfNew <= targetSize; - if (sizeOfNew >= targetSize) { - #idle(cur, ((extra, new), extraSize + sizeOfNew)) // note: aux can be non-empty, thus ignored here, when the target size decreases after pop operations - } else copy - }; - - public func push(common : CommonState, t : T) : CommonState = switch common { - case (#copy(cur, aux, new, sizeOfNew)) #copy(Current.push(cur, t), aux, new, sizeOfNew); - case (#idle(cur, idle)) #idle(Current.push(cur, t), Idle.push(idle, t)) // yes, push to both - }; - - public func pop(common : CommonState) : (T, CommonState) = switch common { - case (#copy(cur, aux, new, sizeOfNew)) { - let (t, cur2) = Current.pop(cur); - (t, norm(#copy(cur2, aux, new, sizeOfNew))) - }; - case (#idle(cur, idle)) { - let (t, idle2) = Idle.pop(idle); - (t, #idle(Current.pop(cur).1, idle2)) - } - }; - - public func peek(common : CommonState) : T = switch common { - case (#copy(cur, _, _, _)) Current.peek(cur); - case (#idle(_, idle)) Idle.peek(idle) - }; - - public func size(common : CommonState) : Nat = switch common { - case (#copy(cur, _, _, _)) Current.size(cur); - case (#idle(_, (_, size))) size - }; - - public func current(common : CommonState) : Current = switch common { - case (#copy(cur, _, _, _)) cur; - case (#idle(cur, _)) cur - } - }; - - type States = ( - direction : Direction, - bigState : BigState, - smallState : SmallState - ); - - module States { - public func step(states : States) : States = switch states { - case (dir, #big1(_, bigTail, _, 0), #small1(currentS, _, auxS)) { - (dir, BigState.step(states.1), #small2(currentS, auxS, bigTail, null, 0)) - }; - case (dir, big, small) (dir, BigState.step(big), SmallState.step(small)) - } - }; - - type Direction = { #left; #right }; - - func idlesInvariant(((l, nL), (r, nR)) : (Idle, Idle)) : Bool = Stacks.size(l) == nL and Stacks.size(r) == nR and 3 * nL >= nR and 3 * nR >= nL; - - type List = Types.Pure.List; - type Iter = Types.Iter; - func unsafeHead(l : List) : T = Option.unwrap(l).0; - func unsafeTail(l : List) : List = Option.unwrap(l).1 -} diff --git a/.mops/core@2.4.0/src/pure/Set.mo b/.mops/core@2.4.0/src/pure/Set.mo deleted file mode 100644 index 020c79a..0000000 --- a/.mops/core@2.4.0/src/pure/Set.mo +++ /dev/null @@ -1,1563 +0,0 @@ -/// Pure (immutable) sets based on order/comparison of elements. -/// A set is a collection of elements without duplicates. -/// The set data structure type is stable and can be used for orthogonal persistence. -/// -/// Example: -/// ```motoko -/// import Set "mo:core/pure/Set"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// let set = Set.fromIter([3, 1, 2, 3].values(), Nat.compare); -/// assert Set.size(set) == 3; -/// assert not Set.contains(set, Nat.compare, 4); -/// let diff = Set.difference(set, set, Nat.compare); -/// assert Set.isEmpty(diff); -/// } -/// ``` -/// -/// These sets are implemented as red-black trees, a balanced binary search tree of ordered elements. -/// -/// The tree data structure internally colors each of its nodes either red or black, -/// and uses this information to balance the tree during modifying operations. -/// -/// Performance: -/// * Runtime: `O(log(n))` worst case cost per insertion, removal, and retrieval operation. -/// * Space: `O(n)` for storing the entire tree. -/// `n` denotes the number of elements (i.e. nodes) stored in the tree. -/// -/// Credits: -/// -/// The core of this implementation is derived from: -/// -/// * Ken Friis Larsen's [RedBlackMap.sml](https://github.com/kfl/mosml/blob/master/src/mosmllib/Redblackmap.sml), which itself is based on: -/// * Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](http://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html). - -import Runtime "../Runtime"; -import List "../List"; // NB: imperative! -import Iter "../Iter"; -import Types "../Types"; -import Nat "../Nat"; -import Order "../Order"; - -module { - - /// Ordered collection of unique elements of the generic type `T`. - /// If type `T` is stable then `Set` is also stable. - /// To ensure that property the `Set` does not have any methods, - /// instead they are gathered in the functor-like class `Operations` (see example there). - - /// @deprecated M0235 - public type Set = Types.Pure.Set; - - /// Red-black tree of nodes with ordered set elements. - /// Leaves are considered implicitly black. - type Tree = Types.Pure.Set.Tree; - - /// Create a set with the elements obtained from an iterator. - /// Potential duplicate elements in the iterator are ignored, i.e. - /// multiple occurrences of an equal element only occur once in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([3, 1, 2, 1].values(), Nat.compare); - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func fromIter(iter : Iter.Iter, compare : (implicit : (T, T) -> Order.Order)) : Set { - var set = empty() : Set; - for (val in iter) { - set := Internal.add(set, compare, val) - }; - set - }; - - /// Convert an iterator into a set. - /// Potential duplicate elements in the iterator are ignored, i.e. - /// multiple occurrences of an equal element only occur once in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = [3, 1, 2, 1].values(); - /// - /// let set = iter.toSet(Nat.compare); - /// - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func toSet(self : Iter.Iter, compare : (implicit : (T, T) -> Order.Order)) : Set { - fromIter(self, compare) - }; - - /// Given a `set` ordered by `compare`, insert the new `element`, - /// returning the new set. - /// - /// Return the set unchanged if the element already exists in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set0 = Set.empty(); - /// let set1 = Set.add(set0, Nat.compare, 2); - /// let set2 = Set.add(set1, Nat.compare, 1); - /// let set3 = Set.add(set2, Nat.compare, 2); - /// assert Iter.toArray(Set.values(set0)) == []; - /// assert Iter.toArray(Set.values(set1)) == [2]; - /// assert Iter.toArray(Set.values(set2)) == [1, 2]; - /// assert Iter.toArray(Set.values(set3)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned set shares with the `set` most of the tree nodes. - /// Garbage collecting one of the sets (e.g. after an assignment `m := Set.add(m, c, e)`) - /// causes collecting `O(log(n))` nodes. - public func add(self : Set, compare : (implicit : (T, T) -> Order.Order), elem : T) : Set = Internal.add(self, compare, elem); - - /// Given `set` ordered by `compare`, insert the new `element`, - /// returning the set extended with `element` and a Boolean indicating - /// if the element was already present in `set`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set0 = Set.empty(); - /// do { - /// let (set1, new1) = Set.insert(set0, Nat.compare, 2); - /// assert new1; - /// let (set2, new2) = Set.insert(set1, Nat.compare, 1); - /// assert new2; - /// let (set3, new3) = Set.insert(set2, Nat.compare, 2); - /// assert not new3; - /// assert Iter.toArray(Set.values(set3)) == [1, 2] - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned set shares with the `set` most of the tree nodes. - /// Garbage collecting one of the sets (e.g. after an assignment `m := Set.add(m, c, e)`) - /// causes collecting `O(log(n))` nodes. - public func insert(self : Set, compare : (implicit : (T, T) -> Order.Order), elem : T) : (Set, Bool) = Internal.insert(self, compare, elem); - - /// Given `set` ordered by `compare` return the set with `element` removed. - /// Return the set unchanged if the element was absent. - /// - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// - /// let set1 = Set.remove(set, Nat.compare, 2); - /// let set2 = Set.remove(set1, Nat.compare, 4); - /// assert Iter.toArray(Set.values(set2)) == [1, 3]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// Note: The returned set shares with `set` most of the tree nodes. - /// Garbage collecting one of the sets (e.g. after an assignment `m := Set.delete(m, c, e)`) - /// causes collecting `O(log(n))` nodes. - public func remove(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Set = Internal.remove(self, compare, element); - - /// Given `set` ordered by `compare`, delete `element` from the set, returning - /// either the set without the element and a Boolean indicating whether - /// whether `element` was contained in `set`. - /// - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// do { - /// let (set1, contained1) = Set.delete(set, Nat.compare, 2); - /// assert contained1; - /// assert Iter.toArray(Set.values(set1)) == [1, 3]; - /// let (set2, contained2) = Set.delete(set1, Nat.compare, 4); - /// assert not contained2; - /// assert Iter.toArray(Set.values(set2)) == [1, 3]; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// Note: The returned set shares with `set` most of the tree nodes. - /// Garbage collecting one of the sets (e.g. after an assignment `m := Set.delete(m, c, e)`) - /// causes collecting `O(log(n))` nodes. - public func delete(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : (Set, Bool) = Internal.delete(self, compare, element); - - /// Tests whether the set contains the provided element. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Bool "mo:core/Bool"; - /// - /// persistent actor { - /// let set = Set.fromIter([3, 1, 2].values(), Nat.compare); - /// - /// assert Set.contains(set, Nat.compare, 1); - /// assert not Set.contains(set, Nat.compare, 4); - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func contains(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Bool = Internal.contains(self.root, compare, element); - - /// Get the maximal element of the set `set` if it is not empty, otherwise returns `null` - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 2, 1].values(), Nat.compare); - /// let set2 = Set.empty(); - /// assert Set.max(set1) == ?2; - /// assert Set.max(set2) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements in the set - public func max(self : Set) : ?T = Internal.max(self.root); - - /// Retrieves the minimum element from the set. - /// If the set is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([2, 0, 1].values(), Nat.compare); - /// let set2 = Set.empty(); - /// assert Set.min(set1) == ?0; - /// assert Set.min(set2) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set. - public func min(self : Set) : ?T = Internal.min(self.root); - - /// Returns a new set that is the union of `set1` and `set2`, - /// i.e. a new set that all the elements that exist in at least on of the two sets. - /// Potential duplicates are ignored, i.e. if the same element occurs in both `set1` - /// and `set2`, it only occurs once in the returned set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let union = Set.union(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(union)) == [1, 2, 3, 4, 5]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(m)`, retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements in the sets, and `m <= n`. - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(m * log(n))` temporary objects that will be collected as garbage. - public func union(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - if (size(self) < size(other)) { - foldLeft(self, other, func(acc : Set, elem : T) : Set { Internal.add(acc, compare, elem) }) - } else { - foldLeft(other, self, func(acc : Set, elem : T) : Set { Internal.add(acc, compare, elem) }) - } - }; - - /// Returns a new set that is the intersection of `set1` and `set2`, - /// i.e. a new set that contains all the elements that exist in both sets. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let intersection = Set.intersection(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(intersection)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(m)` temporary objects that will be collected as garbage. - public func intersection(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - let elems = List.empty(); - if (self.size < other.size) { - Internal.iterate( - self.root, - func(x : T) { - if (Internal.contains(other.root, compare, x)) { - List.add(elems, x) - } - } - ) - } else { - Internal.iterate( - other.root, - func(x : T) { - if (Internal.contains(self.root, compare, x)) { - List.add(elems, x) - } - } - ) - }; - { root = Internal.buildFromSorted(elems); size = List.size(elems) } - }; - - /// Returns a new set that is the difference between `set1` and `other` (`set1` minus `set2`), - /// i.e. a new set that contains all the elements of `set1` that do not exist in `set2`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let difference = Set.difference(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(difference)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(m * log(n))` temporary objects that will be collected as garbage. - public func difference(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - if (size(self) < size(other)) { - let elems = List.empty(); /* imperative! */ - Internal.iterate( - self.root, - func(x : T) { - if (not Internal.contains(other.root, compare, x)) { - List.add(elems, x) - } - } - ); - { root = Internal.buildFromSorted(elems); size = List.size(elems) } - } else { - foldLeft( - other, - self, - func(acc : Set, elem : T) : Set { - if (Internal.contains(acc.root, compare, elem)) { - Internal.remove(acc, compare, elem) - } else { acc } - } - ) - } - }; - - /// Project all elements of the set in a new set. - /// Apply a mapping function to each element in the set and - /// collect the mapped elements in a new mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([3, 1, 2].values(), Nat.compare); - /// - /// let textNumbers = - /// Set.map(numbers, Text.compare, Nat.toText); - /// assert Iter.toArray(Set.values(textNumbers)) == ["1", "2", "3"]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func map(self : Set, compare : (implicit : (T2, T2) -> Order.Order), project : T1 -> T2) : Set = Internal.foldLeft(self.root, empty(), func(acc : Set, elem : T1) : Set { Internal.add(acc, compare, project(elem)) }); - - /// Apply an operation on each element contained in the set. - /// The operation is applied in ascending order of the elements. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// var text = ""; - /// Set.forEach(numbers, func (element) { - /// text #= " " # Nat.toText(element) - /// }); - /// assert text == " 0 1 2 3"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory. - /// where `n` denotes the number of elements stored in the set. - /// - public func forEach(self : Set, operation : T -> ()) { - ignore foldLeft(self, null, func(acc, e) : Null { operation(e); null }) - }; - - /// Filter elements in a new set. - /// Create a copy of the mutable set that only contains the elements - /// that fulfil the criterion function. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let evenNumbers = Set.filter(numbers, Nat.compare, func (number) { - /// number % 2 == 0 - /// }); - /// assert Iter.toArray(Set.values(evenNumbers)) == [0, 2]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func filter(self : Set, compare : (implicit : (T, T) -> Order.Order), criterion : T -> Bool) : Set { - foldLeft>( - self, - empty(), - func(acc, e) { - if (criterion(e)) (add(acc, compare, e)) else acc - } - ) - }; - - /// Filter all elements in the set by also applying a projection to the elements. - /// Apply a mapping function `project` to all elements in the set and collect all - /// elements, for which the function returns a non-null new element. Collect all - /// non-discarded new elements in a new mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([3, 0, 2, 1].values(), Nat.compare); - /// - /// let evenTextNumbers = Set.filterMap(numbers, Text.compare, func (number) { - /// if (number % 2 == 0) { - /// ?Nat.toText(number) - /// } else { - /// null // discard odd numbers - /// } - /// }); - /// assert Iter.toArray(Set.values(evenTextNumbers)) == ["0", "2"]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func filterMap(self : Set, compare : (implicit : (T2, T2) -> Order.Order), project : T1 -> ?T2) : Set { - func combine(acc : Set, elem : T1) : Set { - switch (project(elem)) { - case null { acc }; - case (?elem2) { - Internal.add(acc, compare, elem2) - } - } - }; - Internal.foldLeft(self.root, empty(), combine) - }; - - /// Test whether `set1` is a sub-set of `set2`, i.e. each element in `set1` is - /// also contained in `set2`. Returns `true` if both sets are equal. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([2, 1, 0].values(), Nat.compare); - /// let set3 = Set.fromIter([3, 4].values(), Nat.compare); - /// assert Set.isSubset(set1, set2, Nat.compare); - /// assert not Set.isSubset(set1, set3, Nat.compare); - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets set1 and set2, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func isSubset(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Bool { - if (self.size > other.size) { return false }; - isSubsetHelper(self.root, other.root, compare) - }; - - /// Test whether two sets are equal. - /// Both sets have to be constructed by the same comparison function. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([2, 1].values(), Nat.compare); - /// let set3 = Set.fromIter([2, 1, 0].values(), Nat.compare); - /// assert Set.equal(set1, set2, Nat.compare); - /// assert not Set.equal(set1, set3, Nat.compare); - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets set1 and set2, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func equal(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Bool { - if (self.size != other.size) { return false }; - isSubsetHelper(self.root, other.root, compare) - }; - - func isSubsetHelper(t1 : Tree, t2 : Tree, compare : (T, T) -> Order.Order) : Bool { - switch (t1, t2) { - case (#leaf, _) { true }; - case (_, #leaf) { false }; - case ((#red(t1l, x1, t1r) or #black(t1l, x1, t1r)), (#red(t2l, x2, t2r)) or #black(t2l, x2, t2r)) { - switch (compare(x1, x2)) { - case (#equal) { - isSubsetHelper(t1l, t2l, compare) and isSubsetHelper(t1r, t2r, compare) - }; - // x1 < x2 ==> x1 \in t2l /\ t1l \subset t2l - case (#less) { - Internal.contains(t2l, compare, x1) and isSubsetHelper(t1l, t2l, compare) and isSubsetHelper(t1r, t2, compare) - }; - // x2 < x1 ==> x1 \in t2r /\ t1r \subset t2r - case (#greater) { - Internal.contains(t2r, compare, x1) and isSubsetHelper(t1l, t2, compare) and isSubsetHelper(t1r, t2r, compare) - } - } - } - } - }; - - /// Compare two sets by comparing the elements. - /// Both sets must have been created by the same comparison function. - /// The two sets are iterated by the ascending order of their creation and - /// order is determined by the following rules: - /// Less: - /// `set1` is less than `set2` if: - /// * the pairwise iteration hits an element pair `element1` and `element2` where - /// `element1` is less than `element2` and all preceding elements are equal, or, - /// * `set1` is a strict prefix of `set2`, i.e. `set2` has more elements than `set1` - /// and all elements of `set1` occur at the beginning of iteration `set2`. - /// Equal: - /// `set1` and `set2` have same series of equal elements by pairwise iteration. - /// Greater: - /// `set1` is neither less nor equal `set2`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 1].values(), Nat.compare); - /// let set2 = Set.fromIter([0, 2].values(), Nat.compare); - /// - /// assert Set.compare(set1, set2, Nat.compare) == #less; - /// assert Set.compare(set1, set1, Nat.compare) == #equal; - /// assert Set.compare(set2, set1, Nat.compare) == #greater; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that `compare` has runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func compare(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - // TODO: optimize using recursion on self? - let iterator1 = values(self); - let iterator2 = values(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?element1, ?element2) { - let comparison = compare(element1, element2); - if (comparison != #equal) { - return comparison - } - } - } - } - }; - - /// Returns an iterator over the elements in the set, - /// traversing the elements in the ascending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 3, 1].values(), Nat.compare); - /// - /// var text = ""; - /// for (number in Set.values(set)) { - /// text #= " " # Nat.toText(number); - /// }; - /// assert text == " 0 1 2 3"; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func values(self : Set) : Iter.Iter = Internal.iter(self.root, #fwd); - - /// Returns an iterator over the elements in the set, - /// traversing the elements in the descending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 3, 1].values(), Nat.compare); - /// - /// var tmp = ""; - /// for (number in Set.reverseValues(set)) { - /// tmp #= " " # Nat.toText(number); - /// }; - /// assert tmp == " 3 2 1 0"; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseValues(self : Set) : Iter.Iter = Internal.iter(self.root, #bwd); - - /// Create a new empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Iter.toArray(Set.values(set)) == []; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Set = { root = #leaf; size = 0 }; - - /// Create a new set with a single element. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.singleton(0); - /// assert Iter.toArray(Set.values(set)) == [0]; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func singleton(element : T) : Set { - { - size = 1; - root = #red(#leaf, element, #leaf) - } - }; - - /// Return the number of elements in a set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1, 3].values(), Nat.compare); - /// - /// assert Set.size(set) == 4; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func size(self : Set) : Nat = self.size; - - /// Iterate all elements in ascending order, - /// and accumulate the elements by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1].values(), Nat.compare); - /// - /// let text = Set.foldLeft( - /// set, - /// "", - /// func (accumulator, element) { - /// accumulator # " " # Nat.toText(element) - /// } - /// ); - /// assert text == " 0 1 2 3"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - public func foldLeft( - self : Set, - base : A, - combine : (A, T) -> A - ) : A = Internal.foldLeft(self.root, base, combine); - - /// Iterate all elements in descending order, - /// and accumulate the elements by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1].values(), Nat.compare); - /// - /// let text = Set.foldRight( - /// set, - /// "", - /// func (element, accumulator) { - /// accumulator # " " # Nat.toText(element) - /// } - /// ); - /// assert text == " 3 2 1 0"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - public func foldRight( - self : Set, - base : A, - combine : (T, A) -> A - ) : A = Internal.foldRight(self.root, base, combine); - - /// Determines whether a set is empty. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.empty(); - /// let set2 = Set.singleton(1); - /// - /// assert Set.isEmpty(set1); - /// assert not Set.isEmpty(set2); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func isEmpty(self : Set) : Bool { - switch (self.root) { - case (#leaf) { true }; - case _ { false } - } - }; - - /// Check whether all element in the set satisfy a predicate, i.e. - /// the `predicate` function returns `true` for all elements in the set. - /// Returns `true` for an empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let belowTen = Set.all(set, func (number) { - /// number < 10 - /// }); - /// assert belowTen; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set. - public func all(self : Set, predicate : T -> Bool) : Bool = Internal.all(self.root, predicate); - - /// Check whether at least one element in the set satisfies a predicate, i.e. - /// the `predicate` function returns `true` for at least one element in the set. - /// Returns `false` for an empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let aboveTen = Set.any(set, func (number) { - /// number > 10 - /// }); - /// assert not aboveTen; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func any(self : Set, pred : T -> Bool) : Bool = Internal.any(self.root, pred); - - /// Test helper that check internal invariant for the given set `s`. - /// Raise an error (for a stack trace) if invariants are violated. - public func assertValid(self : Set, compare : (implicit : (T, T) -> Order.Order)) : () { - Internal.assertValid(self, compare) - }; - - /// Generate a textual representation of all the elements in the set. - /// Primarily to be used for testing and debugging. - /// The elements are formatted according to `elementFormat`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// assert Set.toText(set, Nat.toText) == "PureSet{0, 1, 2, 3}"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that `elementFormat` has runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func toText(self : Set, elementFormat : (implicit : (toText : T -> Text))) : Text { - var text = "PureSet{"; - var sep = ""; - for (element in values(self)) { - text #= sep # elementFormat(element); - sep := ", " - }; - text # "}" - }; - - /// Construct the union of a set of element sets, i.e. all elements of - /// each element set are included in the result set. - /// Any duplicates are ignored, i.e. if the same element occurs in multiple element sets, - /// it only occurs once in the result set. - /// - /// Assumes all sets are ordered by `compare`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Order "mo:core/Order"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// func setCompare(first: Set.Set, second: Set.Set) : Order.Order { - /// Set.compare(first, second, Nat.compare) - /// }; - /// - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let set3 = Set.fromIter([5, 6, 7].values(), Nat.compare); - /// let setOfSets = Set.fromIter([set1, set2, set3].values(), setCompare); - /// let flatSet = Set.flatten(setOfSets, Nat.compare); - /// assert Iter.toArray(Set.values(flatSet)) == [1, 2, 3, 4, 5, 6, 7]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in all the sub-sets, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func flatten(self : Set>, compare : (implicit : (T, T) -> Order.Order)) : Set { - var result = empty(); - for (set in values(self)) { - result := union(result, set, compare) - }; - result - }; - - /// Construct the union of a series of sets, i.e. all elements of - /// each set are included in the result set. - /// Any duplicates are ignored, i.e. if an element occurs - /// in several of the iterated sets, it only occurs once in the result set. - /// - /// Assumes all sets are ordered by `compare`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let set3 = Set.fromIter([5, 6, 7].values(), Nat.compare); - /// let combined = Set.join([set1, set2, set3].values(), Nat.compare); - /// assert Iter.toArray(Set.values(combined)) == [1, 2, 3, 4, 5, 6, 7]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the iterated sets, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func join(self : Iter.Iter>, compare : (implicit : (T, T) -> Order.Order)) : Set { - var result = empty(); - for (set in self) { - result := union(result, set, compare) - }; - result - }; - - module Internal { - public func contains(tree : Tree, compare : (T, T) -> Order.Order, elem : T) : Bool { - func f(t : Tree, x : T) : Bool { - switch t { - case (#black(l, x1, r)) { - switch (compare(x, x1)) { - case (#less) { f(l, x) }; - case (#equal) { true }; - case (#greater) { f(r, x) } - } - }; - case (#red(l, x1, r)) { - switch (compare(x, x1)) { - case (#less) { f(l, x) }; - case (#equal) { true }; - case (#greater) { f(r, x) } - } - }; - case (#leaf) { false } - } - }; - f(tree, elem) - }; - - public func max(m : Tree) : ?V { - func rightmost(m : Tree) : V { - switch m { - case (#red(_, v, #leaf)) { v }; - case (#red(_, _, r)) { rightmost(r) }; - case (#black(_, v, #leaf)) { v }; - case (#black(_, _, r)) { rightmost(r) }; - case (#leaf) { Runtime.trap "pure/Set.max() impossible" } - } - }; - switch m { - case (#leaf) { null }; - case (_) { ?rightmost(m) } - } - }; - - public func min(m : Tree) : ?V { - func leftmost(m : Tree) : V { - switch m { - case (#red(#leaf, v, _)) { v }; - case (#red(l, _, _)) { leftmost(l) }; - case (#black(#leaf, v, _)) { v }; - case (#black(l, _, _)) { leftmost(l) }; - case (#leaf) { Runtime.trap "pure/Set.min() impossible" } - } - }; - switch m { - case (#leaf) { null }; - case (_) { ?leftmost(m) } - } - }; - - public func all(m : Tree, pred : V -> Bool) : Bool { - switch m { - case (#red(l, v, r)) { - pred(v) and all(l, pred) and all(r, pred) - }; - case (#black(l, v, r)) { - pred(v) and all(l, pred) and all(r, pred) - }; - case (#leaf) { true } - } - }; - - public func any(m : Tree, pred : V -> Bool) : Bool { - switch m { - case (#red(l, v, r)) { - pred(v) or any(l, pred) or any(r, pred) - }; - case (#black(l, v, r)) { - pred(v) or any(l, pred) or any(r, pred) - }; - case (#leaf) { false } - } - }; - - public func iterate(m : Tree, f : V -> ()) { - switch m { - case (#leaf) {}; - case (#black(l, v, r)) { iterate(l, f); f(v); iterate(r, f) }; - case (#red(l, v, r)) { iterate(l, f); f(v); iterate(r, f) } - } - }; - - // build tree from elements arr[l]..arr[r-1] - public func buildFromSorted(buf : List.List) : Tree { - var maxDepth = 0; - var maxSize = 1; - while (maxSize < List.size(buf)) { - maxDepth += 1; - maxSize += maxSize + 1 - }; - maxDepth := if (maxDepth == 0) { 1 } else { maxDepth }; // keep root black for 1 element tree - func buildFromSortedHelper(l : Nat, r : Nat, depth : Nat) : Tree { - if (l + 1 == r) { - if (depth == maxDepth) { - return #red(#leaf, List.at(buf, l), #leaf) - } else { - return #black(#leaf, List.at(buf, l), #leaf) - } - }; - if (l >= r) { - return #leaf - }; - let m = (l + r) / 2; - return #black( - buildFromSortedHelper(l, m, depth + 1), - List.at(buf, m), - buildFromSortedHelper(m + 1, r, depth + 1) - ) - }; - buildFromSortedHelper(0, List.size(buf), 0) - }; - - type IterRep = Types.Pure.List<{ #tr : Tree; #x : T }>; - - type SetTraverser = (Tree, T, Tree, IterRep) -> IterRep; - - class IterSet(tree : Tree, setTraverser : SetTraverser) { - var trees : IterRep = ?(#tr(tree), null); - public func next() : ?T { - switch (trees) { - case (null) { null }; - case (?(#tr(#leaf), ts)) { - trees := ts; - next() - }; - case (?(#x(x), ts)) { - trees := ts; - ?x - }; - case (?(#tr(#black(l, x, r)), ts)) { - trees := setTraverser(l, x, r, ts); - next() - }; - case (?(#tr(#red(l, x, r)), ts)) { - trees := setTraverser(l, x, r, ts); - next() - } - } - } - }; - - public func iter(s : Tree, direction : { #fwd; #bwd }) : Iter.Iter { - let turnLeftFirst : SetTraverser = func(l, x, r, ts) { - ?(#tr(l), ?(#x(x), ?(#tr(r), ts))) - }; - - let turnRightFirst : SetTraverser = func(l, x, r, ts) { - ?(#tr(r), ?(#x(x), ?(#tr(l), ts))) - }; - - switch direction { - case (#fwd) IterSet(s, turnLeftFirst); - case (#bwd) IterSet(s, turnRightFirst) - } - }; - - public func foldLeft( - tree : Tree, - base : Accum, - combine : (Accum, T) -> Accum - ) : Accum { - switch (tree) { - case (#leaf) { base }; - case (#black(l, x, r)) { - let left = foldLeft(l, base, combine); - let middle = combine(left, x); - foldLeft(r, middle, combine) - }; - case (#red(l, x, r)) { - let left = foldLeft(l, base, combine); - let middle = combine(left, x); - foldLeft(r, middle, combine) - } - } - }; - - public func foldRight( - tree : Tree, - base : Accum, - combine : (T, Accum) -> Accum - ) : Accum { - switch (tree) { - case (#leaf) { base }; - case (#black(l, x, r)) { - let right = foldRight(r, base, combine); - let middle = combine(x, right); - foldRight(l, middle, combine) - }; - case (#red(l, x, r)) { - let right = foldRight(r, base, combine); - let middle = combine(x, right); - foldRight(l, middle, combine) - } - } - }; - - func redden(t : Tree) : Tree { - switch t { - case (#black(l, x, r)) { (#red(l, x, r)) }; - case _ { - Runtime.trap "pure/Set.redden() impossible" - } - } - }; - - func lbalance(left : Tree, x : T, right : Tree) : Tree { - switch (left, right) { - case (#red(#red(l1, x1, r1), x2, r2), r) { - #red( - #black(l1, x1, r1), - x2, - #black(r2, x, r) - ) - }; - case (#red(l1, x1, #red(l2, x2, r2)), r) { - #red( - #black(l1, x1, l2), - x2, - #black(r2, x, r) - ) - }; - case _ { - #black(left, x, right) - } - } - }; - - func rbalance(left : Tree, x : T, right : Tree) : Tree { - switch (left, right) { - case (l, #red(l1, x1, #red(l2, x2, r2))) { - #red( - #black(l, x, l1), - x1, - #black(l2, x2, r2) - ) - }; - case (l, #red(#red(l1, x1, r1), x2, r2)) { - #red( - #black(l, x, l1), - x1, - #black(r1, x2, r2) - ) - }; - case _ { - #black(left, x, right) - } - } - }; - - public func add( - set : Set, - compare : (T, T) -> Order.Order, - elem : T - ) : Set { - insert(set, compare, elem).0 - }; - - public func insert( - s : Set, - compare : (T, T) -> Order.Order, - elem : T - ) : (Set, Bool) { - var newNodeIsCreated : Bool = false; - func ins(tree : Tree) : Tree { - switch tree { - case (#black(left, x, right)) { - switch (compare(elem, x)) { - case (#less) { - lbalance(ins left, x, right) - }; - case (#greater) { - rbalance(left, x, ins right) - }; - case (#equal) { - #black(left, x, right) - } - } - }; - case (#red(left, x, right)) { - switch (compare(elem, x)) { - case (#less) { - #red(ins left, x, right) - }; - case (#greater) { - #red(left, x, ins right) - }; - case (#equal) { - #red(left, x, right) - } - } - }; - case (#leaf) { - newNodeIsCreated := true; - #red(#leaf, elem, #leaf) - } - } - }; - let newRoot = switch (ins(s.root)) { - case (#red(left, x, right)) { - #black(left, x, right) - }; - case other { other } - }; - if newNodeIsCreated ({ root = newRoot; size = s.size + 1 }, true) else (s, false) - }; - - func balLeft(left : Tree, x : T, right : Tree) : Tree { - switch (left, right) { - case (#red(l1, x1, r1), r) { - #red(#black(l1, x1, r1), x, r) - }; - case (_, #black(l2, x2, r2)) { - rbalance(left, x, #red(l2, x2, r2)) - }; - case (_, #red(#black(l2, x2, r2), x3, r3)) { - #red( - #black(left, x, l2), - x2, - rbalance(r2, x3, redden r3) - ) - }; - case _ { Runtime.trap "pure/Set.balLeft() impossible" } - } - }; - - func balRight(left : Tree, x : T, right : Tree) : Tree { - switch (left, right) { - case (l, #red(l1, x1, r1)) { - #red(l, x, #black(l1, x1, r1)) - }; - case (#black(l1, x1, r1), r) { - lbalance(#red(l1, x1, r1), x, r) - }; - case (#red(l1, x1, #black(l2, x2, r2)), r3) { - #red( - lbalance(redden l1, x1, l2), - x2, - #black(r2, x, r3) - ) - }; - case _ { Runtime.trap "pure/Set.balRight() impossible" } - } - }; - - func append(left : Tree, right : Tree) : Tree { - switch (left, right) { - case (#leaf, _) { right }; - case (_, #leaf) { left }; - case ( - #red(l1, x1, r1), - #red(l2, x2, r2) - ) { - switch (append(r1, l2)) { - case (#red(l3, x3, r3)) { - #red( - #red(l1, x1, l3), - x3, - #red(r3, x2, r2) - ) - }; - case r1l2 { - #red(l1, x1, #red(r1l2, x2, r2)) - } - } - }; - case (t1, #red(l2, x2, r2)) { - #red(append(t1, l2), x2, r2) - }; - case (#red(l1, x1, r1), t2) { - #red(l1, x1, append(r1, t2)) - }; - case (#black(l1, x1, r1), #black(l2, x2, r2)) { - switch (append(r1, l2)) { - case (#red(l3, x3, r3)) { - #red( - #black(l1, x1, l3), - x3, - #black(r3, x2, r2) - ) - }; - case r1l2 { - balLeft( - l1, - x1, - #black(r1l2, x2, r2) - ) - } - } - } - } - }; - - public func remove(set : Set, compare : (T, T) -> Order.Order, elem : T) : Set { - delete(set, compare, elem).0 - }; - - public func delete(s : Set, compare : (T, T) -> Order.Order, x : T) : (Set, Bool) { - var changed : Bool = false; - func delNode(left : Tree, x1 : T, right : Tree) : Tree { - switch (compare(x, x1)) { - case (#less) { - let newLeft = del left; - switch left { - case (#black(_, _, _)) { - balLeft(newLeft, x1, right) - }; - case _ { - #red(newLeft, x1, right) - } - } - }; - case (#greater) { - let newRight = del right; - switch right { - case (#black(_, _, _)) { - balRight(left, x1, newRight) - }; - case _ { - #red(left, x1, newRight) - } - } - }; - case (#equal) { - changed := true; - append(left, right) - } - } - }; - func del(tree : Tree) : Tree { - switch tree { - case (#black(left, x1, right)) { - delNode(left, x1, right) - }; - case (#red(left, x1, right)) { - delNode(left, x1, right) - }; - case (#leaf) { - tree - } - } - }; - let newRoot = switch (del(s.root)) { - case (#red(left, x1, right)) { - #black(left, x1, right) - }; - case other { other } - }; - if changed ({ root = newRoot; size = s.size - 1 }, true) else (s, false) - }; - - // check binary search tree order of elements and black depth invariant of the RB-tree - public func assertValid(s : Set, comp : (T, T) -> Order.Order) { - ignore blackDepth(s.root, comp) - }; - - func blackDepth(node : Tree, comp : (T, T) -> Order.Order) : Nat { - func checkNode(left : Tree, x1 : T, right : Tree) : Nat { - checkElem(left, func(x : T) : Bool { comp(x, x1) == #less }); - checkElem(right, func(x : T) : Bool { comp(x, x1) == #greater }); - let leftBlacks = blackDepth(left, comp); - let rightBlacks = blackDepth(right, comp); - assert (leftBlacks == rightBlacks); - leftBlacks - }; - switch node { - case (#leaf) 0; - case (#red(left, x1, right)) { - assert (not isRed(left)); - assert (not isRed(right)); - checkNode(left, x1, right) - }; - case (#black(left, x1, right)) { - checkNode(left, x1, right) + 1 - } - } - }; - - func isRed(node : Tree) : Bool { - switch node { - case (#red(_, _, _)) true; - case _ false - } - }; - - func checkElem(node : Tree, isValid : T -> Bool) { - switch node { - case (#leaf) {}; - case (#black(_, elem, _)) { - assert (isValid(elem)) - }; - case (#red(_, elem, _)) { - assert (isValid(elem)) - } - } - } - }; - -} diff --git a/.mops/core@2.5.0/LICENSE b/.mops/core@2.5.0/LICENSE deleted file mode 100644 index f593a1f..0000000 --- a/.mops/core@2.5.0/LICENSE +++ /dev/null @@ -1,208 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, and - distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by the - copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all other - entities that control, are controlled by, or are under common control with - that entity. For the purposes of this definition, "control" means (i) the - power, direct or indirect, to cause the direction or management of such - entity, whether by contract or otherwise, or (ii) ownership of fifty percent - (50%) or more of the outstanding shares, or (iii) beneficial ownership of - such entity. - - "You" (or "Your") shall mean an individual or Legal Entity exercising - permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation source, and - configuration files. - - "Object" form shall mean any form resulting from mechanical transformation - or translation of a Source form, including but not limited to compiled - object code, generated documentation, and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or Object form, - made available under the License, as indicated by a copyright notice that is - included in or attached to the work (an example is provided in the Appendix - below). - - "Derivative Works" shall mean any work, whether in Source or Object form, - that is based on (or derived from) the Work and for which the editorial - revisions, annotations, elaborations, or other modifications represent, as a - whole, an original work of authorship. For the purposes of this License, - Derivative Works shall not include works that remain separable from, or - merely link (or bind by name) to the interfaces of, the Work and Derivative - Works thereof. - - "Contribution" shall mean any work of authorship, including the original - version of the Work and any modifications or additions to that Work or - Derivative Works thereof, that is intentionally submitted to Licensor for - inclusion in the Work by the copyright owner or by an individual or Legal - Entity authorized to submit on behalf of the copyright owner. For the - purposes of this definition, "submitted" means any form of electronic, - verbal, or written communication sent to the Licensor or its - representatives, including but not limited to communication on electronic - mailing lists, source code control systems, and issue tracking systems that - are managed by, or on behalf of, the Licensor for the purpose of discussing - and improving the Work, but excluding communication that is conspicuously - marked or otherwise designated in writing by the copyright owner as "Not a - Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity on - behalf of whom a Contribution has been received by Licensor and subsequently - incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this - License, each Contributor hereby grants to You a perpetual, worldwide, - non-exclusive, no-charge, royalty-free, irrevocable copyright license to - reproduce, prepare Derivative Works of, publicly display, publicly perform, - sublicense, and distribute the Work and such Derivative Works in Source or - Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this - License, each Contributor hereby grants to You a perpetual, worldwide, - non-exclusive, no-charge, royalty-free, irrevocable (except as stated in - this section) patent license to make, have made, use, offer to sell, sell, - import, and otherwise transfer the Work, where such license applies only to - those patent claims licensable by such Contributor that are necessarily - infringed by their Contribution(s) alone or by combination of their - Contribution(s) with the Work to which such Contribution(s) was submitted. - If You institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work or a - Contribution incorporated within the Work constitutes direct or contributory - patent infringement, then any patent licenses granted to You under this - License for that Work shall terminate as of the date such litigation is - filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or - Derivative Works thereof in any medium, with or without modifications, and - in Source or Object form, provided that You meet the following conditions: - - a. You must give any other recipients of the Work or Derivative Works a - copy of this License; and - - b. You must cause any modified files to carry prominent notices stating - that You changed the files; and - - c. You must retain, in the Source form of any Derivative Works that You - distribute, all copyright, patent, trademark, and attribution notices - from the Source form of the Work, excluding those notices that do not - pertain to any part of the Derivative Works; and - - d. If the Work includes a "NOTICE" text file as part of its distribution, - then any Derivative Works that You distribute must include a readable - copy of the attribution notices contained within such NOTICE file, - excluding those notices that do not pertain to any part of the Derivative - Works, in at least one of the following places: within a NOTICE text file - distributed as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, within a - display generated by the Derivative Works, if and wherever such - third-party notices normally appear. The contents of the NOTICE file are - for informational purposes only and do not modify the License. You may - add Your own attribution notices within Derivative Works that You - distribute, alongside or as an addendum to the NOTICE text from the Work, - provided that such additional attribution notices cannot be construed as - modifying the License. - - You may add Your own copyright statement to Your modifications and may - provide additional or different license terms and conditions for use, - reproduction, or distribution of Your modifications, or for any such - Derivative Works as a whole, provided Your use, reproduction, and - distribution of the Work otherwise complies with the conditions stated in - this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any - Contribution intentionally submitted for inclusion in the Work by You to the - Licensor shall be under the terms and conditions of this License, without - any additional terms or conditions. Notwithstanding the above, nothing - herein shall supersede or modify the terms of any separate license agreement - you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, - trademarks, service marks, or product names of the Licensor, except as - required for reasonable and customary use in describing the origin of the - Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in - writing, Licensor provides the Work (and each Contributor provides its - Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied, including, without limitation, any - warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or - FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining - the appropriateness of using or redistributing the Work and assume any risks - associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in - tort (including negligence), contract, or otherwise, unless required by - applicable law (such as deliberate and grossly negligent acts) or agreed to - in writing, shall any Contributor be liable to You for damages, including - any direct, indirect, special, incidental, or consequential damages of any - character arising as a result of this License or out of the use or inability - to use the Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all other - commercial damages or losses), even if such Contributor has been advised of - the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or - Derivative Works thereof, You may choose to offer, and charge a fee for, - acceptance of support, warranty, indemnity, or other liability obligations - and/or rights consistent with this License. However, in accepting such - obligations, You may act only on Your own behalf and on Your sole - responsibility, not on behalf of any other Contributor, and only if You - agree to indemnify, defend, and hold each Contributor harmless for any - liability incurred by, or claims asserted against, such Contributor by - reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -LLVM EXCEPTIONS TO THE APACHE 2.0 LICENSE - -As an exception, if, as a result of your compiling your source code, portions -of this Software are embedded into an Object form of such source code, you may -redistribute such embedded portions in such Object form without complying with -the conditions of Sections 4(a), 4(b) and 4(d) of the License. - -In addition, if you combine or link compiled forms of this Software with -software that is licensed under the GPLv2 ("Combined Software") and if a court -of competent jurisdiction determines that the patent provision (Section 3), the -indemnity provision (Section 9) or other Section of the License conflicts with -the conditions of the GPLv2, you may retroactively and prospectively choose to -deem waived or otherwise exclude such Section(s) of the License, but only in -their entirety and only with respect to the Combined Software. - -END OF LLVM EXCEPTIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification -within third-party archives. - -Copyright 2025 DFINITY Stiftung - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. - -END OF APPENDIX diff --git a/.mops/core@2.5.0/NOTICE b/.mops/core@2.5.0/NOTICE deleted file mode 100644 index a25e095..0000000 --- a/.mops/core@2.5.0/NOTICE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright 2025 DFINITY Stiftung - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. - -This product contains modified software originally developed by MR Research AG, -used with permission: - -* https://github.com/research-ag/vector -* https://github.com/research-ag/prng diff --git a/.mops/core@2.5.0/README.md b/.mops/core@2.5.0/README.md deleted file mode 100644 index de26d99..0000000 --- a/.mops/core@2.5.0/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# `core` - -* 📦 [Mops Package](https://mops.one/core) -* ✨ [Documentation](https://internetcomputer.org/docs/motoko/core) - ---- - -The `core` package is the official standard library for the [Motoko](https://github.com/dfinity/motoko) programming language. - -This replaces the original `base` library, which is available [here](https://github.com/dfinity/motoko-base). - -An official [migration guide](https://internetcomputer.org/docs/motoko/base-core-migration) is available for upgrading projects from `base` to `core`. - -## Quick Start - -1. Install the [Mops](https://docs.mops.one/quick-start) package manager -2. Open a terminal in your project directory -3. Run `mops add core` - -This adds the following dependency to your `mops.toml` config file: - -```toml -[dependencies] -core = "2.5.0" -``` - -## Contributing - -This repository is currently closed to external contributions. Please feel free to report a bug, ask a question, or request a feature on the project's [GitHub issues](https://github.com/dfinity/motoko-core/issues) page. - -Interface design and code style guidelines for the repository can be found [here](https://github.com/dfinity/motoko-core/blob/main/Styleguide.md). - -### Dev Environment - -> Make sure that [Node.js](https://nodejs.org/en/) `>= 22.x` is installed on your system. - -Run the following commands to configure your local development branch: - -```sh -# First-time setup -git clone https://github.com/dfinity/motoko-core -cd motoko-core -npm ci -npx ic-mops toolchain init -``` - -Below is a quick reference for commonly-used scripts during development: - -```sh -npm test # Run all tests -npm run format # Format Motoko files -npm run validate:api # Update the public API lockfile -npm run validate:docs Array # Run code snippets in `src/Array.mo` -``` - -All available scripts can be found in the project's [`package.json`](https://github.com/dfinity/motoko-core/blob/main/package.json) file. - -### Major Contributors - -Big thanks to the following community contributors: - -* [MR Research AG (A. Stepanov, T. Hanke)](https://github.com/research-ag): [`vector`](https://github.com/research-ag/vector), [`prng`](https://github.com/research-ag/prng) -* [Byron Becker](https://github.com/ByronBecker): [`StableHeapBTreeMap`](https://github.com/canscale/StableHeapBTreeMap) -* [Zen Voich](https://github.com/ZenVoich): [`test`](https://github.com/ZenVoich/test) diff --git a/.mops/core@2.5.0/mops.toml b/.mops/core@2.5.0/mops.toml deleted file mode 100644 index 561ef69..0000000 --- a/.mops/core@2.5.0/mops.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "core" -version = "2.5.0" -description = "The Motoko standard library" -repository = "https://github.com/caffeinelabs/motoko-core" -keywords = [ - "core", - "base", - "data-structure", - "stable-memory", - "persistent" -] -license = "Apache-2.0" - -[dev-dependencies] -test = "2.1.1" -bench = "1.0.0" -fuzz = "1.0.0" -matchers = "2.1.0" -base-0-14-13 = "https://github.com/dfinity/motoko-base#moc-0.14.13@794174a307975c225cfb26b57f73e38a841c0415" -bench-helper = "0.0.3" - -[requirements] -moc = "1.6.0" - -[toolchain] -moc = "1.6.0" -wasmtime = "35.0.0" diff --git a/.mops/core@2.5.0/src/Array.mo b/.mops/core@2.5.0/src/Array.mo deleted file mode 100644 index d833ced..0000000 --- a/.mops/core@2.5.0/src/Array.mo +++ /dev/null @@ -1,1182 +0,0 @@ -/// Provides extended utility functions on immutable Arrays (values of type `[T]`). -/// -/// Note the difference between mutable (`[var T]`) and immutable (`[T]`) arrays. -/// Mutable arrays allow their elements to be modified after creation, while -/// immutable arrays are fixed once created. -/// -/// WARNING: If you are looking for a list that can grow and shrink in size, -/// it is recommended you use `List` for those purposes. -/// Arrays must be created with a fixed size. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Array "mo:core/Array"; -/// ``` - -import Order "Order"; -import VarArray "VarArray"; -import Option "Option"; -import Types "Types"; -import Prim "mo:⛔"; - -module { - - /// Creates an empty array (equivalent to `[]`). - /// - /// ```motoko include=import - /// let array = Array.empty(); - /// assert array == []; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func empty() : [T] = []; - - /// Creates an array containing `item` repeated `size` times. - /// - /// ```motoko include=import - /// let array = Array.repeat("Echo", 3); - /// assert array == ["Echo", "Echo", "Echo"]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func repeat(item : T, size : Nat) : [T] = Prim.Array_tabulate(size, func _ = item); - - /// Creates an immutable array of size `size`. Each element at index i - /// is created by applying `generator` to i. - /// - /// ```motoko include=import - /// let array : [Nat] = Array.tabulate(4, func i = i * 2); - /// assert array == [0, 2, 4, 6]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `generator` runs in O(1) time and space. - public let tabulate : (size : Nat, generator : Nat -> T) -> [T] = Prim.Array_tabulate; - - /// Transforms a mutable array into an immutable array. - /// - /// ```motoko include=import - /// let varArray = [var 0, 1, 2]; - /// varArray[2] := 3; - /// let array = Array.fromVarArray(varArray); - /// assert array == [0, 1, 3]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// @deprecated M0235 - public func fromVarArray(varArray : [var T]) : [T] = Prim.Array_tabulate(varArray.size(), func i = varArray[i]); - - /// Transforms an immutable array into a mutable array. - /// - /// ```motoko include=import - /// import VarArray "mo:core/VarArray"; - /// import Nat "mo:core/Nat"; - /// - /// let array = [0, 1, 2]; - /// let varArray = Array.toVarArray(array); - /// varArray[2] := 3; - /// assert VarArray.equal(varArray, [var 0, 1, 3], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func toVarArray(self : [T]) : [var T] { - let size = self.size(); - if (size == 0) { - return [var] - }; - let newArray = Prim.Array_init(size, self[0]); - var i = 0; - while (i < size) { - newArray[i] := self[i]; - i += 1 - }; - newArray - }; - - /// Tests if two arrays contain equal values (i.e. they represent the same - /// list of elements). Uses `equal` to compare elements in the arrays. - /// - /// ```motoko include=import - /// // Use the equal function from the Nat module to compare Nats - /// import {equal} "mo:core/Nat"; - /// - /// let array1 = [0, 1, 2, 3]; - /// let array2 = [0, 1, 2, 3]; - /// assert Array.equal(array1, array2, equal); - /// ``` - /// - /// Runtime: O(size1 + size2) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func equal(self : [T], other : [T], equal : (implicit : (T, T) -> Bool)) : Bool { - let size1 = self.size(); - let size2 = other.size(); - if (size1 != size2) { - return false - }; - var i = 0; - while (i < size1) { - if (not equal(self[i], other[i])) { - return false - }; - i += 1 - }; - true - }; - - /// Returns the first value in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let array = [1, 9, 4, 8]; - /// let found = Array.find(array, func x = x > 8); - /// assert found == ?9; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func find(self : [T], predicate : T -> Bool) : ?T { - for (element in self.vals()) { - if (predicate(element)) { - return ?element - } - }; - null - }; - - /// Returns the first index in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let array = ['A', 'B', 'C', 'D']; - /// let found = Array.findIndex(array, func(x) { x == 'C' }); - /// assert found == ?2; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func findIndex(self : [T], predicate : T -> Bool) : ?Nat { - for ((index, element) in enumerate(self)) { - if (predicate(element)) { - return ?index - } - }; - null - }; - - /// Create a new array by concatenating the values of `array1` and `array2`. - /// Note that `Array.concat` copies its arguments and has linear complexity. - /// - /// ```motoko include=import - /// let array1 = [1, 2, 3]; - /// let array2 = [4, 5, 6]; - /// let result = Array.concat(array1, array2); - /// assert result == [1, 2, 3, 4, 5, 6]; - /// ``` - /// Runtime: O(size1 + size2) - /// - /// Space: O(size1 + size2) - public func concat(self : [T], other : [T]) : [T] { - let size1 = self.size(); - let size2 = other.size(); - Prim.Array_tabulate( - size1 + size2, - func i { - if (i < size1) { - self[i] - } else { - other[i - size1] - } - } - ) - }; - - /// Sorts the elements in the array according to `compare`. - /// Sort is deterministic and stable. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [4, 2, 6]; - /// let sorted = Array.sort(array, Nat.compare); - /// assert sorted == [2, 4, 6]; - /// ``` - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sort(self : [T], compare : (implicit : (T, T) -> Order.Order)) : [T] { - let varArray : [var T] = toVarArray(self); - VarArray.sortInPlace(varArray, compare); - fromVarArray(varArray) - }; - - /// Creates a new array by reversing the order of elements in `array`. - /// - /// ```motoko include=import - /// let array = [10, 11, 12]; - /// let reversed = Array.reverse(array); - /// assert reversed == [12, 11, 10]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func reverse(self : [T]) : [T] { - let size = self.size(); - Prim.Array_tabulate(size, func i = self[size - i - 1]) - }; - - /// Calls `f` with each element in `array`. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// var sum = 0; - /// let array = [0, 1, 2, 3]; - /// Array.forEach(array, func(x) { - /// sum += x; - /// }); - /// assert sum == 6; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEach(self : [T], f : T -> ()) { - for (item in self.vals()) { - f(item) - } - }; - - /// Creates a new array by applying `f` to each element in `array`. `f` "maps" - /// each element it is applied to of type `X` to an element of type `Y`. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// let array1 = [0, 1, 2, 3]; - /// let array2 = Array.map(array1, func x = x * 2); - /// assert array2 == [0, 2, 4, 6]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func map(self : [T], f : T -> R) : [R] = Prim.Array_tabulate(self.size(), func i = f(self[i])); - - /// Creates a new array by applying `predicate` to every element - /// in `array`, retaining the elements for which `predicate` returns true. - /// - /// ```motoko include=import - /// let array = [4, 2, 6, 1, 5]; - /// let evenElements = Array.filter(array, func x = x % 2 == 0); - /// assert evenElements == [4, 2, 6]; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func filter(self : [T], f : T -> Bool) : [T] { - var count = 0; - let keep = Prim.Array_tabulate( - self.size(), - func i { - if (f(self[i])) { - count += 1; - true - } else { - false - } - } - ); - var nextKeep = 0; - Prim.Array_tabulate( - count, - func _ { - while (not keep[nextKeep]) { - nextKeep += 1 - }; - nextKeep += 1; - self[nextKeep - 1] - } - ) - }; - - /// Creates a new array by applying `f` to each element in `array`, - /// and keeping all non-null elements. The ordering is retained. - /// - /// ```motoko include=import - /// import {toText} "mo:core/Nat"; - /// - /// let array = [4, 2, 0, 1]; - /// let newArray = - /// Array.filterMap( // mapping from Nat to Text values - /// array, - /// func x = if (x == 0) { null } else { ?toText(100 / x) } // can't divide by 0, so return null - /// ); - /// assert newArray == ["25", "50", "100"]; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func filterMap(self : [T], f : T -> ?R) : [R] { - var count = 0; - let options = Prim.Array_tabulate( - self.size(), - func i { - let result = f(self[i]); - switch (result) { - case (?element) { - count += 1; - result - }; - case null { - null - } - } - } - ); - - var nextSome = 0; - Prim.Array_tabulate( - count, - func _ { - while (Option.isNull(options[nextSome])) { - nextSome += 1 - }; - nextSome += 1; - switch (options[nextSome - 1]) { - case (?element) element; - case null { - Prim.trap "Array.filterMap(): malformed array" - } - } - } - ) - }; - - /// Creates a new array by applying `f` to each element in `array`. - /// If any invocation of `f` produces an `#err`, returns an `#err`. Otherwise - /// returns an `#ok` containing the new array. - /// - /// ```motoko include=import - /// let array = [4, 3, 2, 1, 0]; - /// // divide 100 by every element in the array - /// let result = Array.mapResult(array, func x { - /// if (x > 0) { - /// #ok(100 / x) - /// } else { - /// #err "Cannot divide by zero" - /// } - /// }); - /// assert result == #err "Cannot divide by zero"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - /// @deprecated M0235 - public func mapResult(self : [T], f : T -> Types.Result) : Types.Result<[R], E> { - let size = self.size(); - - var error : ?Types.Result<[R], E> = null; - let results = Prim.Array_tabulate( - size, - func i { - switch (f(self[i])) { - case (#ok element) { - ?element - }; - case (#err e) { - switch (error) { - case null { - // only take the first error - error := ?(#err e) - }; - case _ {} - }; - null - } - } - } - ); - - switch error { - case null { - // unpack the option - #ok( - map( - results, - func element { - switch element { - case (?element) { - element - }; - case null { - Prim.trap "Array.mapResult(): malformed array" - } - } - } - ) - ) - }; - case (?error) { - error - } - } - }; - - /// Creates a new array by applying `f` to each element in `array` and its index. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// let array = [10, 10, 10, 10]; - /// let newArray = Array.mapEntries(array, func (x, i) = i * x); - /// assert newArray == [0, 10, 20, 30]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapEntries(self : [T], f : (T, Nat) -> R) : [R] = Prim.Array_tabulate(self.size(), func i = f(self[i], i)); - - /// Creates a new array by applying `k` to each element in `array`, - /// and concatenating the resulting arrays in order. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4]; - /// let newArray = Array.flatMap(array, func x = [x, -x].values()); - /// assert newArray == [1, -1, 2, -2, 3, -3, 4, -4]; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `k` runs in O(1) time and space. - public func flatMap(self : [T], k : T -> Types.Iter) : [R] { - var flatSize = 0; - let arrays = Prim.Array_tabulate<[R]>( - self.size(), - func i { - let subArray = fromIter(k(self[i])); - flatSize += subArray.size(); - subArray - } - ); - - // could replace with a call to flatten, - // but it would require an extra pass (to compute `flatSize`) - var outer = 0; - var inner = 0; - Prim.Array_tabulate( - flatSize, - func _ { - while (inner == arrays[outer].size()) { - inner := 0; - outer += 1 - }; - let element = arrays[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// ```motoko include=import - /// import {add} "mo:core/Nat"; - /// - /// let array = [4, 2, 0, 1]; - /// let sum = - /// Array.foldLeft( - /// array, - /// 0, // start the sum at 0 - /// func(sumSoFar, x) = sumSoFar + x // this entire function can be replaced with `add`! - /// ); - /// assert sum == 7; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(self : [T], base : A, combine : (A, T) -> A) : A { - var acc = base; - for (element in self.values()) { - acc := combine(acc, element) - }; - acc - }; - - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// ```motoko include=import - /// import {toText} "mo:core/Nat"; - /// - /// let array = [1, 9, 4, 8]; - /// let bookTitle = Array.foldRight(array, "", func(x, acc) = toText(x) # acc); - /// assert bookTitle == "1948"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldRight(self : [T], base : A, combine : (T, A) -> A) : A { - var acc = base; - let size = self.size(); - var i = size; - while (i > 0) { - i -= 1; - acc := combine(self[i], acc) - }; - acc - }; - - /// Combines an iterator of arrays into a single array. Retains the original - /// ordering of the elements. - /// - /// Consider using `Array.flatten()` for better performance. - /// - /// ```motoko include=import - /// let arrays = [[0, 1, 2], [2, 3], [], [4]]; - /// let joinedArray = Array.join(arrays.values()); - /// assert joinedArray == [0, 1, 2, 2, 3, 4]; - /// ``` - /// - /// Runtime: O(number of elements in array) - /// - /// Space: O(number of elements in array) - public func join(self : Types.Iter<[T]>) : [T] { - flatten(fromIter(self)) - }; - - /// Combines an array of arrays into a single array. Retains the original - /// ordering of the elements. - /// - /// This has better performance compared to `Array.join()`. - /// - /// ```motoko include=import - /// let arrays = [[0, 1, 2], [2, 3], [], [4]]; - /// let flatArray = Array.flatten(arrays); - /// assert flatArray == [0, 1, 2, 2, 3, 4]; - /// ``` - /// - /// Runtime: O(number of elements in array) - /// - /// Space: O(number of elements in array) - public func flatten(self : [[T]]) : [T] { - var flatSize = 0; - for (subArray in self.vals()) { - flatSize += subArray.size() - }; - - var outer = 0; - var inner = 0; - Prim.Array_tabulate( - flatSize, - func _ { - while (inner == self[outer].size()) { - inner := 0; - outer += 1 - }; - let element = self[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Create an array containing a single value. - /// - /// ```motoko include=import - /// let array = Array.singleton(2); - /// assert array == [2]; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func singleton(element : T) : [T] = [element]; - - /// Returns the size of an array. Equivalent to `array.size()`. - public func size(self : [T]) : Nat = self.size(); - - /// Returns whether an array is empty, i.e. contains zero elements. - public func isEmpty(self : [T]) : Bool = self.size() == 0; - - /// Converts an iterator to an array. - /// @deprecated M0235 - public func fromIter(iter : Types.Iter) : [T] { - var list : Types.Pure.List = null; - var size = 0; - label l loop { - switch (iter.next()) { - case (?element) { - list := ?(element, list); - size += 1 - }; - case null { break l } - } - }; - if (size == 0) { return [] }; - let array = Prim.Array_init( - size, - switch list { - case (?(h, _)) h; - case null { - Prim.trap("Array.fromIter(): unreachable") - } - } - ); - var i = size : Nat; - while (i > 0) { - i -= 1; - switch list { - case (?(h, t)) { - array[i] := h; - list := t - }; - case null { - Prim.trap("Array.fromIter(): unreachable") - } - } - }; - Prim.Array_tabulate(size, func i = array[i]) - }; - - /// Returns an iterator (`Iter`) over the indices of `array`. - /// An iterator provides a single method `next()`, which returns - /// indices in order, or `null` when out of index to iterate over. - /// - /// Note: You can also use `array.keys()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// let array = [10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.keys()) { - /// sum += element; - /// }; - /// assert sum == 3; // 0 + 1 + 2 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func keys(self : [T]) : Types.Iter = self.keys(); - - /// Iterator provides a single method `next()`, which returns - /// elements in order, or `null` when out of elements to iterate over. - /// - /// Note: You can also use `array.values()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// let array = [10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.values()) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func values(self : [T]) : Types.Iter = self.values(); - - /// Iterator provides a single method `next()`, which returns - /// pairs of (index, element) in order, or `null` when out of elements to iterate over. - /// - /// ```motoko include=import - /// let array = [10, 11, 12]; - /// - /// var sum = 0; - /// for ((index, element) in Array.enumerate(array)) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func enumerate(self : [T]) : Types.Iter<(Nat, T)> = object { - let size = self.size(); - var index = 0; - public func next() : ?(Nat, T) { - if (index >= size) { - return null - }; - let i = index; - index += 1; - ?(i, self[i]) - } - }; - - /// Returns true if all elements in `array` satisfy the predicate function. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4]; - /// assert Array.all(array, func x = x > 0); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func all(self : [T], predicate : T -> Bool) : Bool { - for (element in self.values()) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Returns true if any element in `array` satisfies the predicate function. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4]; - /// assert Array.any(array, func x = x > 3); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func any(self : [T], predicate : T -> Bool) : Bool { - for (element in self.values()) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Returns the index of the first `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.indexOf(array, Char.equal, 'c') == ?0; - /// assert Array.indexOf(array, Char.equal, 'f') == ?2; - /// assert Array.indexOf(array, Char.equal, 'g') == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func indexOf(self : [T], equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = nextIndexOf(self, equal, element, 0); - - /// Returns the index of the next occurence of `element` in the `array` starting from the `from` index (inclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.nextIndexOf(array, Char.equal, 'c', 0) == ?0; - /// assert Array.nextIndexOf(array, Char.equal, 'f', 0) == ?2; - /// assert Array.nextIndexOf(array, Char.equal, 'f', 2) == ?2; - /// assert Array.nextIndexOf(array, Char.equal, 'f', 3) == ?3; - /// assert Array.nextIndexOf(array, Char.equal, 'f', 4) == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func nextIndexOf(self : [T], equal : (implicit : (T, T) -> Bool), element : T, fromInclusive : Nat) : ?Nat { - var index = fromInclusive; - let size = self.size(); - while (index < size) { - if (equal(self[index], element)) { - return ?index - } else { - index += 1 - } - }; - null - }; - - /// Returns the index of the last `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.lastIndexOf(array, Char.equal, 'c') == ?0; - /// assert Array.lastIndexOf(array, Char.equal, 'f') == ?3; - /// assert Array.lastIndexOf(array, Char.equal, 'e') == ?5; - /// assert Array.lastIndexOf(array, Char.equal, 'g') == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func lastIndexOf(self : [T], equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = prevIndexOf(self, equal, element, self.size()); - - /// Returns the index of the previous occurence of `element` in the `array` starting from the `from` index (exclusive). - /// - /// Negative indices are relative to the end of the array. For example, `-1` corresponds to the last element in the array. - /// - /// If the indices are out of bounds, they are clamped to the array bounds. - /// If the first index is greater than the second, the function returns an empty iterator. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.prevIndexOf(array, Char.equal, 'c', array.size()) == ?0; - /// assert Array.prevIndexOf(array, Char.equal, 'e', array.size()) == ?5; - /// assert Array.prevIndexOf(array, Char.equal, 'e', 5) == ?4; - /// assert Array.prevIndexOf(array, Char.equal, 'e', 4) == null; - /// ``` - /// - /// Runtime: O(array.size()); - /// Space: O(1); - public func prevIndexOf(self : [T], equal : (implicit : (T, T) -> Bool), element : T, fromExclusive : Nat) : ?Nat { - var i = fromExclusive; - while (i > 0) { - i -= 1; - if (equal(self[i], element)) { - return ?i - } - }; - null - }; - - /// Returns true if the `array` contains `element` using the provided `equal` function. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = ['c', 'o', 'f', 'f', 'e', 'e']; - /// assert Array.contains(array, Char.equal, 'f'); - /// assert not Array.contains(array, Char.equal, 'g'); - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func contains(self : [T], equal : (implicit : (T, T) -> Bool), element : T) : Bool { - for (item in self.vals()) { - if (equal(item, element)) { - return true - } - }; - false - }; - - /// Returns an iterator over a slice of `array` starting at `fromInclusive` up to (but not including) `toExclusive`. - /// - /// Negative indices are relative to the end of the array. For example, `-1` corresponds to the last element in the array. - /// - /// If the indices are out of bounds, they are clamped to the array bounds. - /// If the first index is greater than the second, the function returns an empty iterator. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4, 5]; - /// let iter1 = Array.range(array, 3, array.size()); - /// assert iter1.next() == ?4; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// let iter2 = Array.range(array, 3, -1); - /// assert iter2.next() == ?4; - /// assert iter2.next() == null; - /// - /// let iter3 = Array.range(array, 0, 0); - /// assert iter3.next() == null; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func range(self : [T], fromInclusive : Int, toExclusive : Int) : Types.Iter { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - object { - var pos = start; - public func next() : ?T { - if (pos >= end) { - null - } else { - let elem = self[pos]; - pos += 1; - ?elem - } - } - } - }; - - /// Returns a new array containing elements from `array` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// let array = [1, 2, 3, 4, 5]; - /// - /// let slice1 = Array.sliceToArray(array, 1, 4); - /// assert slice1 == [2, 3, 4]; - /// - /// let slice2 = Array.sliceToArray(array, 1, -1); - /// assert slice2 == [2, 3, 4]; - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToArray(self : [T], fromInclusive : Int, toExclusive : Int) : [T] { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - if (start >= end) { - return [] - }; - Prim.Array_tabulate(end - start, func i = self[start + i]) - }; - - /// Returns a new mutable array containing elements from `array` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// import VarArray "mo:core/VarArray"; - /// import Nat "mo:core/Nat"; - /// - /// let array = [1, 2, 3, 4, 5]; - /// - /// let slice1 = Array.sliceToVarArray(array, 1, 4); - /// assert VarArray.equal(slice1, [var 2, 3, 4], Nat.equal); - /// - /// let slice2 = Array.sliceToVarArray(array, 1, -1); - /// assert VarArray.equal(slice2, [var 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToVarArray(self : [T], fromInclusive : Int, toExclusive : Int) : [var T] { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - if (start >= end) { - return [var] - }; - Prim.Array_tabulateVar(end - start, func i = self[start + i]) - }; - - /// Converts the array to its textual representation using `f` to convert each element to `Text`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [1, 2, 3]; - /// let text = Array.toText(array, Nat.toText); - /// assert text == "[1, 2, 3]"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func toText(self : [T], f : (implicit : (toText : T -> Text))) : Text { - let size = self.size(); - if (size == 0) { return "[]" }; - var text = "["; - var i = 0; - while (i < size) { - if (i != 0) { - text #= ", " - }; - text #= f(self[i]); - i += 1 - }; - text #= "]"; - text - }; - - /// Compares two arrays using the provided comparison function for elements. - /// Returns #less, #equal, or #greater if `array1` is less than, equal to, - /// or greater than `array2` respectively. - /// - /// If arrays have different sizes but all elements up to the shorter length are equal, - /// the shorter array is considered #less than the longer array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array1 = [1, 2, 3]; - /// let array2 = [1, 2, 4]; - /// assert Array.compare(array1, array2, Nat.compare) == #less; - /// ``` - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array3 = [1, 2]; - /// let array4 = [1, 2, 3]; - /// assert Array.compare(array3, array4, Nat.compare) == #less; - /// ``` - /// - /// Runtime: O(min(size1, size2)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func compare(self : [T], other : [T], compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let size1 = self.size(); - let size2 = other.size(); - var i = 0; - let minSize = if (size1 < size2) { size1 } else { size2 }; - while (i < minSize) { - switch (compare(self[i], other[i])) { - case (#less) { return #less }; - case (#greater) { return #greater }; - case (#equal) { i += 1 } - } - }; - if (size1 < size2) { #less } else if (size1 > size2) { #greater } else { - #equal - } - }; - - /// Performs binary search on a sorted array to find the index of the `element`. - /// - /// Returns `#found(index)` if the element is found, or `#insertionIndex(index)` with the index - /// where the element would be inserted according to the ordering if not found. - /// - /// If there are multiple equal elements, no guarantee is made about which index is returned. - /// The array must be sorted in ascending order according to the `compare` function. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let sorted = [1, 3, 5, 7, 9, 11]; - /// assert Array.binarySearch(sorted, Nat.compare, 5) == #found(2); - /// assert Array.binarySearch(sorted, Nat.compare, 6) == #insertionIndex(3); - /// ``` - /// - /// Runtime: O(log(size)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func binarySearch(self : [T], compare : (implicit : (T, T) -> Order.Order), element : T) : { - #found : Nat; - #insertionIndex : Nat - } { - var left = 0; - var right = self.size(); - while (left < right) { - let mid = (left + right) / 2; - switch (compare(self[mid], element)) { - case (#less) left := mid + 1; - case (#greater) right := mid; - case (#equal) return #found mid - } - }; - #insertionIndex left - }; - - /// Checks whether the `array` is sorted according to the `compare` function. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [1, 2, 3]; - /// assert Array.isSorted(array, Nat.compare); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func isSorted(self : [T], compare : (implicit : (T, T) -> Order.Order)) : Bool { - let size = self.size(); - if (size <= 1) return true; - var i = 1; - while (i < size) { - switch (compare(self[i - 1], self[i])) { - case (#greater) return false; - case _ { i += 1 } - } - }; - true - } -} diff --git a/.mops/core@2.5.0/src/Base64.mo b/.mops/core@2.5.0/src/Base64.mo deleted file mode 100644 index 97e1bc8..0000000 --- a/.mops/core@2.5.0/src/Base64.mo +++ /dev/null @@ -1,138 +0,0 @@ -/// Module for Base64 encoding of byte sequences. -/// -/// Base64 encoding converts binary data to an ASCII string using 64 printable -/// characters, as specified in [RFC 4648](https://www.rfc-editor.org/rfc/rfc4648). -/// It is widely used for HTTP Basic Authentication, encoding binary data in -/// JSON payloads, and data URIs. -/// -/// This module uses the standard Base64 alphabet (`A–Z`, `a–z`, `0–9`, `+`, `/`) -/// and pads output to a multiple of 4 characters using `=`. -/// -/// Original version authored by Claude Sonnet (claude-sonnet-4-6) for use in generated -/// Motoko API clients. The module received subsequent manual performance improvements. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Base64 "mo:core/Base64"; -/// ``` - -import Blob "Blob"; -import Nat8 "Nat8"; -import Nat16 "Nat16"; -import Nat32 "Nat32"; -import Nat64 "Nat64"; -import Text "Text"; -import Prim "mo:prim"; - -module { - - // Standard Base64 alphabet (RFC 4648 §4) in UTF8 values. - // Equivalent to Text form: - /* - private let alphabet : [Text] = [ - "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", - "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", - "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", - "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/" - ]; - */ - // prettier-ignore - private let alphabet : [Nat8] = [ - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, - 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, - 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, - 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - 43, 47 - ]; - - /// Encodes a `Blob` as a Base64 `Text` string (RFC 4648 §4). - /// - /// Output length is always a multiple of 4, padded with `=` as needed. - /// An empty `Blob` encodes to an empty `Text`. - /// - /// Example: - /// ```motoko include=import - /// assert Base64.encode("" : Blob) == ""; - /// assert Base64.encode("f" : Blob) == "Zg=="; - /// assert Base64.encode("fo" : Blob) == "Zm8="; - /// assert Base64.encode("foo" : Blob) == "Zm9v"; - /// assert Base64.encode("foobar" : Blob) == "Zm9vYmFy"; - /// ``` - /// - /// Typical use — embedding text in a data URI: - /// ```motoko include=import - /// let payload = "Hello" : Blob; - /// let uri = "data:text/plain;base64," # Base64.encode(payload); - /// assert uri == "data:text/plain;base64,SGVsbG8="; - /// ``` - public func encode(data : Blob) : Text { - let sz = Nat64.fromIntWrap(data.size()); - var result = ""; - var i = 0 : Nat64; - var next_i = 6 : Nat64; - - // Process chunks of 6 input bytes at a time (8 output characters) - while (next_i <= sz) { - let b1 = data[i.toNat()]; - let b2 : Nat8 = data[(i +% 1).toNat()]; - let b3 : Nat8 = data[(i +% 2).toNat()]; - let b4 : Nat8 = data[(i +% 3).toNat()]; - let b5 : Nat8 = data[(i +% 4).toNat()]; - let b6 : Nat8 = data[(i +% 5).toNat()]; - - let n = (b1.toNat16().toNat32() << 16) | (b2.toNat16().toNat32() << 8) | b3.toNat16().toNat32(); - let m = (b4.toNat16().toNat32() << 16) | (b5.toNat16().toNat32() << 8) | b6.toNat16().toNat32(); - - let bytes = Blob.fromArray([ - alphabet[((n >> 18) & 0x3F).toNat()], - alphabet[((n >> 12) & 0x3F).toNat()], - alphabet[((n >> 6) & 0x3F).toNat()], - alphabet[(n & 0x3F).toNat()], - alphabet[((m >> 18) & 0x3F).toNat()], - alphabet[((m >> 12) & 0x3F).toNat()], - alphabet[((m >> 6) & 0x3F).toNat()], - alphabet[(m & 0x3F).toNat()] - ]); - - switch (Text.decodeUtf8(bytes)) { - case (?t) result := result # t; - case (_) { - Prim.trap("Cannot happen: Utf8 decode error in Base64.encode().") - } - }; - - i := next_i; - next_i +%= 6 - }; - - // Process remaining 0-5 input bytes in chunks of 3 - while (i < sz) { - let b1 = data[i.toNat()]; - let b2 : Nat8 = if (i +% 1 < sz) data[(i +% 1).toNat()] else 0; - let b3 : Nat8 = if (i +% 2 < sz) data[(i +% 2).toNat()] else 0; - - let n = (b1.toNat16().toNat32() << 16) | (b2.toNat16().toNat32() << 8) | b3.toNat16().toNat32(); - - // Note: Value 61 is the UTF8 encoding of the `=` character - let bytes = Blob.fromArray([ - alphabet[((n >> 18) & 0x3F).toNat()], - alphabet[((n >> 12) & 0x3F).toNat()], - if (i +% 1 < sz) alphabet[((n >> 6) & 0x3F).toNat()] else 61, - if (i +% 2 < sz) alphabet[(n & 0x3F).toNat()] else 61 - ]); - - switch (Text.decodeUtf8(bytes)) { - case (?t) result := result # t; - case (_) { - Prim.trap("Cannot happen: Utf8 decode error in Base64.encode().") - } - }; - - i +%= 3 - }; - result - }; - -} diff --git a/.mops/core@2.5.0/src/Blob.mo b/.mops/core@2.5.0/src/Blob.mo deleted file mode 100644 index 64de595..0000000 --- a/.mops/core@2.5.0/src/Blob.mo +++ /dev/null @@ -1,242 +0,0 @@ -/// Module for working with Blobs (immutable sequences of bytes). -/// -/// Blobs represent sequences of bytes. They are immutable, iterable, but not indexable and can be empty. -/// -/// Byte sequences are also often represented as `[Nat8]`, i.e. an array of bytes, but this representation is currently much less compact than `Blob`, taking 4 physical bytes to represent each logical byte in the sequence. -/// If you would like to manipulate Blobs, it is recommended that you convert -/// Blobs to `[var Nat8]` or `Buffer`, do the manipulation, then convert back. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Blob "mo:core/Blob"; -/// ``` -/// -/// Some built in features not listed in this module: -/// -/// * You can create a `Blob` literal from a `Text` literal, provided the context expects an expression of type `Blob`. -/// * `b.size() : Nat` returns the number of bytes in the blob `b`; -/// * `b.values() : Iter.Iter` returns an iterator to enumerate the bytes of the blob `b`. -/// -/// For example: -/// ```motoko include=import -/// import Debug "mo:core/Debug"; -/// import Nat8 "mo:core/Nat8"; -/// -/// let blob = "\00\00\00\ff" : Blob; // blob literals, where each byte is delimited by a back-slash and represented in hex -/// let blob2 = "charsもあり" : Blob; // you can also use characters in the literals -/// let numBytes = blob.size(); -/// assert numBytes == 4; // returns the number of bytes in the Blob -/// for (byte in blob.values()) { // iterator over the Blob -/// Debug.print(Nat8.toText(byte)) -/// } -/// ``` - -import Types "Types"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - public type Blob = Prim.Types.Blob; - - /// Returns an empty `Blob` (equivalent to `""`). - /// - /// Example: - /// ```motoko include=import - /// let emptyBlob = Blob.empty(); - /// assert emptyBlob.size() == 0; - /// ``` - public func empty() : Blob = ""; - - /// Returns whether the given `Blob` is empty (has a size of zero). - /// - /// ```motoko include=import - /// let blob1 = "" : Blob; - /// let blob2 = "\FF\00" : Blob; - /// assert Blob.isEmpty(blob1); - /// assert not Blob.isEmpty(blob2); - /// ``` - public func isEmpty(self : Blob) : Bool = self == ""; - - /// Returns the number of bytes in the given `Blob`. - /// This is equivalent to `blob.size()`. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\FF\00\AA" : Blob; - /// assert Blob.size(blob) == 3; - /// assert blob.size() == 3; - /// ``` - public func size(self : Blob) : Nat = self.size(); - - /// Creates a `Blob` from an array of bytes (`[Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// let bytes : [Nat8] = [0, 255, 0]; - /// let blob = Blob.fromArray(bytes); - /// assert blob == "\00\FF\00"; - /// ``` - public let fromArray : (bytes : [Nat8]) -> Blob = Prim.arrayToBlob; - - /// Creates a `Blob` from a mutable array of bytes (`[var Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// let bytes : [var Nat8] = [var 0, 255, 0]; - /// let blob = Blob.fromVarArray(bytes); - /// assert blob == "\00\FF\00"; - /// ``` - public let fromVarArray : (bytes : [var Nat8]) -> Blob = Prim.arrayMutToBlob; - - /// Converts a `Blob` to an array of bytes (`[Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\00\FF\00" : Blob; - /// let bytes = Blob.toArray(blob); - /// assert bytes == [0, 255, 0]; - /// ``` - public let toArray : (self : Blob) -> [Nat8] = Prim.blobToArray; - - /// Converts a `Blob` to a mutable array of bytes (`[var Nat8]`), by copying each element. - /// - /// Example: - /// ```motoko include=import - /// import Nat8 "mo:core/Nat8"; - /// import VarArray "mo:core/VarArray"; - /// - /// let blob = "\00\FF\00" : Blob; - /// let bytes = Blob.toVarArray(blob); - /// assert VarArray.equal(bytes, [var 0, 255, 0], Nat8.equal); - /// ``` - public let toVarArray : (self : Blob) -> [var Nat8] = Prim.blobToArrayMut; - - /// Returns the (non-cryptographic) hash of `blob`. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\00\FF\00" : Blob; - /// let h = Blob.hash(blob); - /// assert h == 1_818_567_776; - /// ``` - public let hash : (self : Blob) -> Types.Hash = Prim.hashBlob; - - /// General purpose comparison function for `Blob` by comparing the value of - /// the bytes. Returns the `Order` (either `#less`, `#equal`, or `#greater`) - /// by comparing `blob1` with `blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\00\00" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// let result = Blob.compare(blob1, blob2); - /// assert result == #less; - /// ``` - public func compare(self : Blob, other : Blob) : Order.Order { - let c = Prim.blobCompare(self, other); - if (c < 0) #less else if (c == 0) #equal else #greater - }; - - /// Equality function for `Blob` types. - /// This is equivalent to `blob1 == blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\FF\00" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// assert Blob.equal(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function value - /// to pass to a higher order function. - /// - /// Example: - /// ```motoko include=import - /// import List "mo:core/List"; - /// - /// let list1 = List.singleton("\00\FF\00"); - /// let list2 = List.singleton("\00\FF\00"); - /// assert List.equal(list1, list2, Blob.equal); - /// ``` - public func equal(self : Blob, other : Blob) : Bool { self == other }; - - /// Inequality function for `Blob` types. - /// This is equivalent to `blob1 != blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\AA\AA" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// assert Blob.notEqual(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func notEqual(self : Blob, other : Blob) : Bool { self != other }; - - /// "Less than" function for `Blob` types. - /// This is equivalent to `blob1 < blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\AA\AA" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// assert Blob.less(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func less(self : Blob, other : Blob) : Bool { self < other }; - - /// "Less than or equal to" function for `Blob` types. - /// This is equivalent to `blob1 <= blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\00\AA\AA" : Blob; - /// let blob2 = "\00\FF\00" : Blob; - /// assert Blob.lessOrEqual(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func lessOrEqual(self : Blob, other : Blob) : Bool { self <= other }; - - /// "Greater than" function for `Blob` types. - /// This is equivalent to `blob1 > blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\BB\AA\AA" : Blob; - /// let blob2 = "\00\00\00" : Blob; - /// assert Blob.greater(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func greater(self : Blob, other : Blob) : Bool { self > other }; - - /// "Greater than or equal to" function for `Blob` types. - /// This is equivalent to `blob1 >= blob2`. - /// - /// Example: - /// ```motoko include=import - /// let blob1 = "\BB\AA\AA" : Blob; - /// let blob2 = "\00\00\00" : Blob; - /// assert Blob.greaterOrEqual(blob1, blob2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func greaterOrEqual(self : Blob, other : Blob) : Bool { - self >= other - }; - -} diff --git a/.mops/core@2.5.0/src/Bool.mo b/.mops/core@2.5.0/src/Bool.mo deleted file mode 100644 index b62e053..0000000 --- a/.mops/core@2.5.0/src/Bool.mo +++ /dev/null @@ -1,126 +0,0 @@ -/// Boolean type and operations. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Bool "mo:core/Bool"; -/// ``` -/// -/// While boolean operators `_ and _` and `_ or _` are short-circuiting, -/// avoiding computation of the right argument when possible, the functions -/// `logicalAnd(_, _)` and `logicalOr(_, _)` are *strict* and will always evaluate *both* -/// of their arguments. -/// -/// Example: -/// ```motoko include=import -/// let t = true; -/// let f = false; -/// -/// // Short-circuiting AND -/// assert not (t and f); -/// -/// // Short-circuiting OR -/// assert t or f; -/// ``` - -import Prim "mo:⛔"; -import Iter "Iter"; -import Order "Order"; - -module { - - /// Booleans with constants `true` and `false`. - public type Bool = Prim.Types.Bool; - - /// Returns `a and b`. - /// - /// Example: - /// ```motoko include=import - /// assert not Bool.logicalAnd(true, false); - /// assert Bool.logicalAnd(true, true); - /// ``` - public func logicalAnd(self : Bool, other : Bool) : Bool = self and other; - - /// Returns `a or b`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.logicalOr(true, false); - /// assert Bool.logicalOr(false, true); - /// ``` - public func logicalOr(self : Bool, other : Bool) : Bool = self or other; - - /// Returns exclusive or of `a` and `b`, `a != b`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.logicalXor(true, false); - /// assert not Bool.logicalXor(true, true); - /// assert not Bool.logicalXor(false, false); - /// ``` - public func logicalXor(self : Bool, other : Bool) : Bool = self != other; - - /// Returns `not bool`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.logicalNot(false); - /// assert not Bool.logicalNot(true); - /// ``` - public func logicalNot(self : Bool) : Bool = not self; - - /// Returns `a == b`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.equal(true, true); - /// assert not Bool.equal(true, false); - /// ``` - public func equal(self : Bool, other : Bool) : Bool { self == other }; - - /// Returns the ordering of `a` compared to `b`. - /// Returns `#less` if `a` is `false` and `b` is `true`, - /// `#equal` if `a` equals `b`, - /// and `#greater` if `a` is `true` and `b` is `false`. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.compare(true, false) == #greater; - /// assert Bool.compare(true, true) == #equal; - /// assert Bool.compare(false, true) == #less; - /// ``` - public func compare(self : Bool, other : Bool) : Order.Order { - if (self == other) #equal else if self #greater else #less - }; - - /// Returns a text value which is either `"true"` or `"false"` depending on the input value. - /// - /// Example: - /// ```motoko include=import - /// assert Bool.toText(true) == "true"; - /// assert Bool.toText(false) == "false"; - /// ``` - public func toText(self : Bool) : Text { - if self "true" else "false" - }; - - /// Returns an iterator over all possible boolean values (`true` and `false`). - /// - /// Example: - /// ```motoko include=import - /// let iter = Bool.allValues(); - /// assert iter.next() == ?true; - /// assert iter.next() == ?false; - /// assert iter.next() == null; - /// ``` - public func allValues() : Iter.Iter = object { - var state : ?Bool = ?true; - public func next() : ?Bool { - switch state { - case (?true) { state := ?false; ?true }; - case (?false) { state := null; ?false }; - case null { null } - } - } - }; - -} diff --git a/.mops/core@2.5.0/src/CallerAttributes.mo b/.mops/core@2.5.0/src/CallerAttributes.mo deleted file mode 100644 index 685ea3d..0000000 --- a/.mops/core@2.5.0/src/CallerAttributes.mo +++ /dev/null @@ -1,52 +0,0 @@ -/// Allows accessing the Internet Computer's caller attributes. -/// TODO: link to official documentation, once it's available. -/// -/// ```motoko name=import -/// import CallerAttributes "mo:core/CallerAttributes"; -/// ``` - -import Prim "mo:⛔"; -import Text "Text"; -import Iter "Iter"; -import Runtime "Runtime"; -import Principal "Principal"; - -module { - /// Returns the attribute data attached to the current call, but only - /// when the signer is listed in the `trusted_attribute_signers` - /// canister environment variable. - /// - /// Returns `null` if the current call carries no caller attributes. - /// Traps if the signer isn't trusted. - /// - /// `trusted_attribute_signers` is expected to be a comma-separated list - /// of principal texts, for example: - /// `"aaaaa-aa,un4fu-tqaaa-aaaab-qadjq-cai"`. - /// - /// Example: - /// ```motoko include=import no-validate - /// persistent actor { - /// public shared func handle() : async () { - /// switch (CallerAttributes.getAttributes()) { - /// case (?data) { /* attributes came from a trusted signer */ }; - /// case null { /* no attributes, or signer is not trusted */ }; - /// }; - /// }; - /// } - /// ``` - public func getAttributes() : ?Blob { - let signerBlob : Blob = Prim.callerInfoSigner(); - // An empty signer means no attributes where sent in this call. - if (signerBlob.size() == 0) { - return null - }; - let signer = Principal.fromBlob(signerBlob); - let ?trustedSigners = Runtime.envVar("trusted_attribute_signers") else { - Runtime.trap("trusted_attribute_signers environment variable is not set") - }; - if (trustedSigners.split(#char(',')).any(func(t) { Principal.fromText(t) == signer })) { - return ?Prim.callerInfoData() - }; - Runtime.trap("untrusted attribute signer") - } -} diff --git a/.mops/core@2.5.0/src/CertifiedData.mo b/.mops/core@2.5.0/src/CertifiedData.mo deleted file mode 100644 index f3ffb82..0000000 --- a/.mops/core@2.5.0/src/CertifiedData.mo +++ /dev/null @@ -1,54 +0,0 @@ -/// Certified data. -/// -/// The Internet Computer allows canister smart contracts to store a small amount of data during -/// update method processing so that during query call processing, the canister can obtain -/// a certificate about that data. -/// -/// This module provides a _low-level_ interface to this API, aimed at advanced -/// users and library implementors. See the Internet Computer Functional -/// Specification and corresponding documentation for how to use this to make query -/// calls to your canister tamperproof. - -import Prim "mo:⛔"; - -module { - - /// Set the certified data. - /// - /// Must be called from an update method, else traps. - /// Must be passed a blob of at most 32 bytes, else traps. - /// - /// Example: - /// ```motoko no-repl - /// import CertifiedData "mo:core/CertifiedData"; - /// import Blob "mo:core/Blob"; - /// - /// // Must be in an update call - /// - /// let array : [Nat8] = [1, 2, 3]; - /// let blob = Blob.fromArray(array); - /// CertifiedData.set(blob); - /// ``` - /// - /// See a full example on how to use certified variables here: https://github.com/dfinity/examples/tree/master/motoko/cert-var - /// - public let set : (data : Blob) -> () = Prim.setCertifiedData; - - /// Gets a certificate - /// - /// Returns `null` if no certificate is available, e.g. when processing an - /// update call or inter-canister call. This returns a non-`null` value only - /// when processing a query call. - /// - /// Example: - /// ```motoko no-repl - /// import CertifiedData "mo:core/CertifiedData"; - /// // Must be in a query call - /// - /// CertifiedData.getCertificate(); - /// ``` - /// See a full example on how to use certified variables here: https://github.com/dfinity/examples/tree/master/motoko/cert-var - /// - public let getCertificate : () -> ?Blob = Prim.getCertificate; - -} diff --git a/.mops/core@2.5.0/src/Char.mo b/.mops/core@2.5.0/src/Char.mo deleted file mode 100644 index 4dc75f5..0000000 --- a/.mops/core@2.5.0/src/Char.mo +++ /dev/null @@ -1,216 +0,0 @@ -/// Module for working with Characters (Unicode code points). -/// -/// Characters in Motoko represent Unicode code points -/// in the range 0 to 0x10FFFF, excluding the surrogate code points -/// (0xD800 through 0xDFFF). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Char "mo:core/Char"; -/// ``` -/// -/// Some built in features not listed in this module: -/// -/// * You can create a `Char` literal using single quotes, e.g. 'A', '1', '漢' -/// * You can compare characters using `<`, `<=`, `==`, `!=`, `>=`, `>` operators -/// * You can convert a single-character `Text` to a `Char` using `:Char` type annotation -/// -/// For example: -/// ```motoko include=import -/// let char : Char = 'A'; -/// let unicodeChar = '漢'; -/// let digit = '7'; -/// assert Char.isDigit(digit); -/// assert Char.toText(char) == "A"; -/// ``` - -import Prim "mo:⛔"; - -module { - - /// Characters represented as Unicode code points. - public type Char = Prim.Types.Char; - - /// Convert character `char` to a word containing its Unicode scalar value. - /// - /// Example: - /// ```motoko include=import - /// let char = 'A'; - /// let unicode = Char.toNat32(char); - /// assert unicode == 65; - /// ``` - public let toNat32 : (self : Char) -> Nat32 = Prim.charToNat32; - - /// Convert `w` to a character. - /// Traps if `w` is not a valid Unicode scalar value. - /// Value `w` is valid if, and only if, `w < 0xD800 or (0xE000 <= w and w <= 0x10FFFF)`. - /// - /// Example: - /// ```motoko include=import - /// let unicode : Nat32 = 65; - /// let char = Char.fromNat32(unicode); - /// assert char == 'A'; - /// ``` - public let fromNat32 : (nat32 : Nat32) -> Char = Prim.nat32ToChar; - - /// Convert character `char` to single character text. - /// - /// Example: - /// ```motoko include=import - /// let char = '漢'; - /// let text = Char.toText(char); - /// assert text == "漢"; - /// ``` - public let toText : (self : Char) -> Text = Prim.charToText; - - // Not exposed pending multi-char implementation. - private let _toUpper : (char : Char) -> Char = Prim.charToUpper; - - // Not exposed pending multi-char implementation. - private let _toLower : (char : Char) -> Char = Prim.charToLower; - - /// Returns `true` when `char` is a decimal digit between `0` and `9`, otherwise `false`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isDigit('5'); - /// assert not Char.isDigit('A'); - /// ``` - public func isDigit(self : Char) : Bool { - Prim.charToNat32(self) -% Prim.charToNat32('0') <= (9 : Nat32) - }; - - /// Returns whether `char` is a whitespace character. - /// Whitespace characters include space, tab, newline, etc. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isWhitespace(' '); - /// assert Char.isWhitespace('\n'); - /// assert not Char.isWhitespace('A'); - /// ``` - public let isWhitespace : (self : Char) -> Bool = Prim.charIsWhitespace; - - /// Returns whether `char` is a lowercase character. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isLower('a'); - /// assert not Char.isLower('A'); - /// ``` - public let isLower : (self : Char) -> Bool = Prim.charIsLowercase; - - /// Returns whether `char` is an uppercase character. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isUpper('A'); - /// assert not Char.isUpper('a'); - /// ``` - public let isUpper : (self : Char) -> Bool = Prim.charIsUppercase; - - /// Returns whether `char` is an alphabetic character. - /// - /// Example: - /// ```motoko include=import - /// assert Char.isAlphabetic('A'); - /// assert Char.isAlphabetic('漢'); - /// assert not Char.isAlphabetic('1'); - /// ``` - public func isAlphabetic(self : Char) : Bool = Prim.charIsAlphabetic(self); - - /// Returns `a == b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.equal('A', 'A'); - /// assert not Char.equal('A', 'B'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func equal(self : Char, other : Char) : Bool { self == other }; - - /// Returns `a != b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.notEqual('A', 'B'); - /// assert not Char.notEqual('A', 'A'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func notEqual(self : Char, other : Char) : Bool { self != other }; - - /// Returns `a < b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.less('A', 'B'); - /// assert not Char.less('B', 'A'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func less(self : Char, other : Char) : Bool { self < other }; - - /// Returns `a <= b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.lessOrEqual('A', 'A'); - /// assert Char.lessOrEqual('A', 'B'); - /// assert not Char.lessOrEqual('B', 'A'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func lessOrEqual(self : Char, other : Char) : Bool { self <= other }; - - /// Returns `a > b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.greater('B', 'A'); - /// assert not Char.greater('A', 'B'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func greater(self : Char, other : Char) : Bool { self > other }; - - /// Returns `a >= b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.greaterOrEqual('B', 'A'); - /// assert Char.greaterOrEqual('A', 'A'); - /// assert not Char.greaterOrEqual('A', 'B'); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function value - /// to pass to a higher order function. - public func greaterOrEqual(self : Char, other : Char) : Bool { self >= other }; - - /// Returns the order of `a` and `b`. - /// - /// Example: - /// ```motoko include=import - /// assert Char.compare('A', 'B') == #less; - /// assert Char.compare('B', 'A') == #greater; - /// assert Char.compare('A', 'A') == #equal; - /// ``` - public func compare(self : Char, other : Char) : { #less; #equal; #greater } { - if (self < other) { #less } else if (self == other) { #equal } else { - #greater - } - }; - -} diff --git a/.mops/core@2.5.0/src/Cycles.mo b/.mops/core@2.5.0/src/Cycles.mo deleted file mode 100644 index 5c62828..0000000 --- a/.mops/core@2.5.0/src/Cycles.mo +++ /dev/null @@ -1,139 +0,0 @@ -/// Managing cycles within actors in the Internet Computer Protocol (ICP). -/// -/// The usage of the Internet Computer is measured, and paid for, in _cycles_. -/// This library provides imperative operations for observing cycles, transferring cycles, and -/// observing refunds of cycles. -/// -/// **NOTE:** Since cycles measure computational resources, the value of `balance()` can change from one call to the next. -/// -/// Cycles can be transferred from the current actor to another actor with the evaluation of certain forms of expression. -/// In particular, the expression must be a call to a shared function, a call to a local function with an `async` return type, or a simple `async` expression. -/// To attach an amount of cycles to an expression ``, simply prefix the expression with `(with cycles = )`, that is, `(with cycles = ) `. -/// -/// **NOTE:** Attaching cycles will trap if the amount specified exceeds `2 ** 128` cycles. -/// -/// Upon the call, but not before, the amount of cycles is deducted from `balance()`. -/// If this total exceeds `balance()`, the caller traps, aborting the call without consuming the cycles. -/// Note that attaching cycles to a call to a local function call or `async` expression just transfers cycles from the current actor to itself. -/// -/// Example for use on the ICP: -/// ```motoko no-repl -/// import Cycles "mo:core/Cycles"; -/// -/// persistent actor { -/// public func main() : async () { -/// let initialBalance = Cycles.balance(); -/// await (with cycles = 15_000_000) operation(); // accepts 10_000_000 cycles -/// assert Cycles.refunded() == 5_000_000; -/// assert Cycles.balance() < initialBalance; // decreased by around 10_000_000 -/// }; -/// -/// func operation() : async () { -/// let initialBalance = Cycles.balance(); -/// let initialAvailable = Cycles.available(); -/// let obtained = Cycles.accept(10_000_000); -/// assert obtained == 10_000_000; -/// assert Cycles.balance() == initialBalance + 10_000_000; -/// assert Cycles.available() == initialAvailable - 10_000_000; -/// } -/// } -/// ``` -import Prim "mo:⛔"; -module { - - /// Returns the actor's current balance of cycles as `amount`. - /// - /// Example for use on the ICP: - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// public func main() : async() { - /// let balance = Cycles.balance(); - /// assert balance > 0; - /// } - /// } - /// ``` - public let balance : () -> (amount : Nat) = Prim.cyclesBalance; - - /// Returns the currently available `amount` of cycles. - /// The amount available is the amount received in the current call, - /// minus the cumulative amount `accept`ed by this call. - /// On exit from the current shared function or async expression via `return` or `throw`, - /// any remaining available amount is automatically refunded to the caller/context. - /// - /// Example for use on the ICP: - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// public func main() : async() { - /// let available = Cycles.available(); - /// assert available >= 0; - /// } - /// } - /// ``` - public let available : () -> (amount : Nat) = Prim.cyclesAvailable; - - /// Transfers up to `amount` from `available()` to `balance()`. - /// Returns the amount actually transferred, which may be less than - /// requested, for example, if less is available, or if canister balance limits are reached. - /// - /// Example for use on the ICP (for simplicity, only transferring cycles to itself): - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// public func main() : async() { - /// await (with cycles = 15_000_000) operation(); // accepts 10_000_000 cycles - /// }; - /// - /// func operation() : async() { - /// let obtained = Cycles.accept(10_000_000); - /// assert obtained == 10_000_000; - /// } - /// } - /// ``` - public let accept : (amount : Nat) -> (accepted : Nat) = Prim.cyclesAccept; - - /// Reports `amount` of cycles refunded in the last `await` of the current - /// context, or zero if no await has occurred yet. - /// Calling `refunded()` is solely informational and does not affect `balance()`. - /// Instead, refunds are automatically added to the current balance, - /// whether or not `refunded` is used to observe them. - /// - /// Example for use on the ICP (for simplicity, only transferring cycles to itself): - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// func operation() : async() { - /// ignore Cycles.accept(10_000_000); - /// }; - /// - /// public func main() : async() { - /// await (with cycles = 15_000_000) operation(); // accepts 10_000_000 cycles - /// assert Cycles.refunded() == 5_000_000; - /// } - /// } - /// ``` - public let refunded : () -> (amount : Nat) = Prim.cyclesRefunded; - - /// Attempts to burn `amount` of cycles, deducting `burned` from the canister's - /// cycle balance. The burned cycles are irrevocably lost and not available to any - /// other principal either. - /// - /// Example for use on the IC: - /// ```motoko no-repl - /// import Cycles "mo:core/Cycles"; - /// - /// persistent actor { - /// public func main() : async() { - /// let burnt = Cycles.burn(10_000_000); - /// assert burnt == 10_000_000; - /// } - /// } - /// ``` - public let burn : (amount : Nat) -> (burned : Nat) = Prim.cyclesBurn; - -} diff --git a/.mops/core@2.5.0/src/Debug.mo b/.mops/core@2.5.0/src/Debug.mo deleted file mode 100644 index 7727a8a..0000000 --- a/.mops/core@2.5.0/src/Debug.mo +++ /dev/null @@ -1,39 +0,0 @@ -/// Utility functions for debugging. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Debug "mo:core/Debug"; -/// ``` - -import Prim "mo:⛔"; -import Runtime "Runtime"; - -module { - - /// Prints `text` to output stream. - /// - /// NOTE: When running on an ICP network, all output is written to the [canister log](https://internetcomputer.org/docs/building-apps/canister-management/logs) with the exclusion of any output - /// produced during the execution of non-replicated queries and composite queries. - /// In other environments, like the interpreter and stand-alone wasm engines, the output is written to standard out. - /// - /// ```motoko include=import - /// Debug.print "Hello New World!"; - /// Debug.print(debug_show(4)) // Often used with `debug_show` to convert values to Text - /// ``` - public let print : (text : Text) -> () = Prim.debugPrint; - - /// Mark incomplete code with the `todo()` function. - /// - /// Each have calls are well-typed in all typing contexts, which - /// trap in all execution contexts. - /// - /// ```motoko include=import - /// func doSomethingComplex() { - /// Debug.todo() - /// }; - /// ``` - public func todo() : None { - Runtime.trap("Debug.todo()") - }; - -} diff --git a/.mops/core@2.5.0/src/Error.mo b/.mops/core@2.5.0/src/Error.mo deleted file mode 100644 index cf73496..0000000 --- a/.mops/core@2.5.0/src/Error.mo +++ /dev/null @@ -1,106 +0,0 @@ -/// Error values and inspection. -/// -/// The `Error` type is the argument to `throw`, parameter of `catch`. -/// The `Error` type is opaque. - -import Prim "mo:⛔"; - -module { - - /// Error value resulting from `async` computations - public type Error = Prim.Types.Error; - - /// Error code to classify different kinds of user and system errors: - /// ```motoko - /// type ErrorCode = { - /// // Fatal error. - /// #system_fatal; - /// // Transient error. - /// #system_transient; - /// // Destination invalid. - /// #destination_invalid; - /// // Canister error (e.g., trap, no response). - /// #canister_error; - /// // Explicit reject by canister code. - /// #canister_reject; - /// // Response unknown; system stopped waiting for it (e.g., timed out, or system under high load). - /// #system_unknown; - /// // Future error code (with unrecognized numeric code). - /// #future : Nat32; - /// // Error issuing inter-canister call - /// // (indicating destination queue full or freezing threshold crossed). - /// #call_error : { err_code : Nat32 } - /// }; - /// ``` - public type ErrorCode = Prim.ErrorCode; - - /// Create an error from the message with the code `#canister_reject`. - /// - /// Example: - /// ```motoko - /// import Error "mo:core/Error"; - /// - /// Error.reject("Example error") // can be used as throw argument - /// ``` - public let reject : (message : Text) -> Error = Prim.error; - - /// Returns the code of an error. - /// - /// Example: - /// ```motoko - /// import Error "mo:core/Error"; - /// - /// let error = Error.reject("Example error"); - /// Error.code(error) // #canister_reject - /// ``` - public let code : (self : Error) -> ErrorCode = Prim.errorCode; - - /// Returns the message of an error. - /// - /// Example: - /// ```motoko - /// import Error "mo:core/Error"; - /// - /// let error = Error.reject("Example error"); - /// Error.message(error) // "Example error" - /// ``` - public let message : (self : Error) -> Text = Prim.errorMessage; - - /// Checks if the error is a clean reject. - /// A clean reject means that there must be no state changes on the callee side. - public func isCleanReject(self : Error) : Bool = switch (code(self)) { - case (#system_fatal or #system_transient or #destination_invalid or #call_error _) true; - case _ false - }; - - /// Returns whether retrying to send a message may result in success. - /// - /// Example: - /// ```motoko - /// import Error "mo:core/Error"; - /// import Debug "mo:core/Debug"; - /// - /// persistent actor { - /// type CallableActor = actor { - /// call : () -> async () - /// }; - /// - /// public func example(callableActor : CallableActor) { - /// try { - /// await (with timeout = 3) callableActor.call(); - /// } - /// catch e { - /// if (Error.isRetryPossible e) { - /// Debug.print(Error.message e); - /// } - /// } - /// } - /// } - /// - /// ``` - public func isRetryPossible(self : Error) : Bool = switch (code(self)) { - case (#system_transient or #system_unknown) true; - case _ false - }; - -} diff --git a/.mops/core@2.5.0/src/Float.mo b/.mops/core@2.5.0/src/Float.mo deleted file mode 100644 index 9618fec..0000000 --- a/.mops/core@2.5.0/src/Float.mo +++ /dev/null @@ -1,829 +0,0 @@ -/// Double precision (64-bit) floating-point numbers in IEEE 754 representation. -/// -/// This module contains common floating-point constants and utility functions. -/// -/// ```motoko name=import -/// import Float "mo:core/Float"; -/// ``` -/// -/// Notation for special values in the documentation below: -/// `+inf`: Positive infinity -/// `-inf`: Negative infinity -/// `NaN`: "not a number" (can have different sign bit values, but `NaN != NaN` regardless of the sign). -/// -/// Note: -/// Floating point numbers have limited precision and operations may inherently result in numerical errors. -/// -/// Examples of numerical errors: -/// ```motoko -/// assert 0.1 + 0.1 + 0.1 != 0.3; -/// ``` -/// -/// ```motoko -/// assert not (1e16 + 1.0 != 1e16); -/// ``` -/// -/// (and many more cases) -/// -/// Advice: -/// * Floating point number comparisons by `==` or `!=` are discouraged. Instead, it is better to compare -/// floating-point numbers with a numerical tolerance, called epsilon. -/// -/// Example: -/// ```motoko -/// import Float "mo:core/Float"; -/// let x = 0.1 + 0.1 + 0.1; -/// let y = 0.3; -/// -/// let epsilon = 1e-6; // This depends on the application case (needs a numerical error analysis). -/// assert Float.equal(x, y, epsilon); -/// ``` -/// -/// * For absolute precision, it is recommened to encode the fraction number as a pair of a Nat for the base -/// and a Nat for the exponent (decimal point). -/// -/// NaN sign: -/// * The NaN sign is only applied by `abs`, `neg`, and `copySign`. Other operations can have an arbitrary -/// sign bit for NaN results. - -import Prim "mo:⛔"; -import Int "Int"; -import Order "Order"; - -module { - - /// 64-bit floating point number type. - public type Float = Prim.Types.Float; - - /// Ratio of the circumference of a circle to its diameter. - /// Note: Limited precision. - public let pi : Float = 3.14159265358979323846; // taken from musl math.h - - /// Base of the natural logarithm. - /// Note: Limited precision. - public let e : Float = 2.7182818284590452354; // taken from musl math.h - - /// Determines whether the `number` is a `NaN` ("not a number" in the floating point representation). - /// Notes: - /// * Equality test of `NaN` with itself or another number is always `false`. - /// * There exist many internal `NaN` value representations, such as positive and negative NaN, - /// signalling and quiet NaNs, each with many different bit representations. - /// - /// Example: - /// ```motoko include=import - /// assert Float.isNaN(0.0/0.0); - /// ``` - public func isNaN(self : Float) : Bool { - self != self - }; - - /// Returns the absolute value of `x`. - /// - /// Special cases: - /// ``` - /// abs(+inf) => +inf - /// abs(-inf) => +inf - /// abs(-NaN) => +NaN - /// abs(-0.0) => 0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.abs(-1.2), 1.2, epsilon); - /// ``` - public let abs : (x : Float) -> Float = Prim.floatAbs; - - /// Returns the square root of `x`. - /// - /// Special cases: - /// ``` - /// sqrt(+inf) => +inf - /// sqrt(-0.0) => -0.0 - /// sqrt(x) => NaN if x < 0.0 - /// sqrt(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.sqrt(6.25), 2.5, epsilon); - /// ``` - public let sqrt : (x : Float) -> Float = Prim.floatSqrt; - - /// Returns the smallest integral float greater than or equal to `x`. - /// - /// Special cases: - /// ``` - /// ceil(+inf) => +inf - /// ceil(-inf) => -inf - /// ceil(NaN) => NaN - /// ceil(0.0) => 0.0 - /// ceil(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.ceil(1.2), 2.0, epsilon); - /// ``` - public let ceil : (x : Float) -> Float = Prim.floatCeil; - - /// Returns the largest integral float less than or equal to `x`. - /// - /// Special cases: - /// ``` - /// floor(+inf) => +inf - /// floor(-inf) => -inf - /// floor(NaN) => NaN - /// floor(0.0) => 0.0 - /// floor(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.floor(1.2), 1.0, epsilon); - /// ``` - public let floor : (x : Float) -> Float = Prim.floatFloor; - - /// Returns the nearest integral float not greater in magnitude than `x`. - /// This is equivalent to returning `x` with truncating its decimal places. - /// - /// Special cases: - /// ``` - /// trunc(+inf) => +inf - /// trunc(-inf) => -inf - /// trunc(NaN) => NaN - /// trunc(0.0) => 0.0 - /// trunc(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.trunc(2.75), 2.0, epsilon); - /// ``` - public let trunc : (x : Float) -> Float = Prim.floatTrunc; - - /// Returns the nearest integral float to `x`. - /// A decimal place of exactly .5 is rounded to the nearest even integral float. - /// and rounded down for `x < 0` - /// - /// Special cases: - /// ``` - /// nearest(+inf) => +inf - /// nearest(-inf) => -inf - /// nearest(NaN) => NaN - /// nearest(0.0) => 0.0 - /// nearest(-0.0) => -0.0 - /// nearest(14.5) => 14.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.nearest(2.75) == 3.0 - /// ``` - public let nearest : (x : Float) -> Float = Prim.floatNearest; - - /// Returns `x` if `x` and `y` have same sign, otherwise `x` with negated sign. - /// - /// The sign bit of zero, infinity, and `NaN` is considered. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.copySign(1.2, -2.3), -1.2, epsilon); - /// ``` - public let copySign : (x : Float, y : Float) -> Float = Prim.floatCopySign; - - /// Returns the smaller value of `x` and `y`. - /// - /// Special cases: - /// ``` - /// min(NaN, y) => NaN for any Float y - /// min(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.min(1.2, -2.3) == -2.3; // with numerical imprecision - /// ``` - public let min : (x : Float, y : Float) -> Float = Prim.floatMin; - - /// Returns the larger value of `x` and `y`. - /// - /// Special cases: - /// ``` - /// max(NaN, y) => NaN for any Float y - /// max(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.max(1.2, -2.3) == 1.2; - /// ``` - public let max : (x : Float, y : Float) -> Float = Prim.floatMax; - - /// Returns the sine of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// sin(+inf) => NaN - /// sin(-inf) => NaN - /// sin(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.sin(Float.pi / 2), 1.0, epsilon); - /// ``` - public let sin : (x : Float) -> Float = Prim.sin; - - /// Returns the cosine of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// cos(+inf) => NaN - /// cos(-inf) => NaN - /// cos(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.cos(Float.pi / 2), 0.0, epsilon); - /// ``` - public let cos : (x : Float) -> Float = Prim.cos; - - /// Returns the tangent of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// tan(+inf) => NaN - /// tan(-inf) => NaN - /// tan(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.tan(Float.pi / 4), 1.0, epsilon); - /// ``` - public let tan : (x : Float) -> Float = Prim.tan; - - /// Returns the arc sine of `x` in radians. - /// - /// Special cases: - /// ``` - /// arcsin(x) => NaN if x > 1.0 - /// arcsin(x) => NaN if x < -1.0 - /// arcsin(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.arcsin(1.0), Float.pi / 2, epsilon); - /// ``` - public let arcsin : (x : Float) -> Float = Prim.arcsin; - - /// Returns the arc cosine of `x` in radians. - /// - /// Special cases: - /// ``` - /// arccos(x) => NaN if x > 1.0 - /// arccos(x) => NaN if x < -1.0 - /// arcos(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.arccos(1.0), 0.0, epsilon); - /// ``` - public let arccos : (x : Float) -> Float = Prim.arccos; - - /// Returns the arc tangent of `x` in radians. - /// - /// Special cases: - /// ``` - /// arctan(+inf) => pi / 2 - /// arctan(-inf) => -pi / 2 - /// arctan(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.arctan(1.0), Float.pi / 4, epsilon); - /// ``` - public let arctan : (x : Float) -> Float = Prim.arctan; - - /// Given `(y, x)`, returns the arc tangent in radians of `y/x` based on the signs of both values to determine the correct quadrant. - /// - /// Special cases: - /// ``` - /// arctan2(0.0, 0.0) => 0.0 - /// arctan2(-0.0, 0.0) => -0.0 - /// arctan2(0.0, -0.0) => pi - /// arctan2(-0.0, -0.0) => -pi - /// arctan2(+inf, +inf) => pi / 4 - /// arctan2(+inf, -inf) => 3 * pi / 4 - /// arctan2(-inf, +inf) => -pi / 4 - /// arctan2(-inf, -inf) => -3 * pi / 4 - /// arctan2(NaN, x) => NaN for any Float x - /// arctan2(y, NaN) => NaN for any Float y - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let sqrt2over2 = Float.sqrt(2) / 2; - /// assert Float.arctan2(sqrt2over2, sqrt2over2) == Float.pi / 4; - /// ``` - public let arctan2 : (y : Float, x : Float) -> Float = Prim.arctan2; - - /// Returns the value of `e` raised to the `x`-th power. - /// - /// Special cases: - /// ``` - /// exp(+inf) => +inf - /// exp(-inf) => 0.0 - /// exp(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.exp(1.0), Float.e, epsilon); - /// ``` - public let exp : (x : Float) -> Float = Prim.exp; - - /// Returns the natural logarithm (base-`e`) of `x`. - /// - /// Special cases: - /// ``` - /// log(0.0) => -inf - /// log(-0.0) => -inf - /// log(x) => NaN if x < 0.0 - /// log(+inf) => +inf - /// log(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.log(Float.e), 1.0, epsilon); - /// ``` - public let log : (x : Float) -> Float = Prim.log; - - /// Formatting. `format(fmt, x)` formats `x` to `Text` according to the - /// formatting directive `fmt`, which can take one of the following forms: - /// - /// * `#fix prec` as fixed-point format with `prec` digits - /// * `#exp prec` as exponential format with `prec` digits - /// * `#gen prec` as generic format with `prec` digits - /// * `#exact` as exact format that can be decoded without loss. - /// - /// `-0.0` is formatted with negative sign bit. - /// Positive infinity is formatted as "inf". - /// Negative infinity is formatted as "-inf". - /// - /// The numerical precision and the text format can vary between - /// Motoko versions and runtime configuration. Moreover, `NaN` can be printed - /// differently, i.e. "NaN" or "nan", potentially omitting the `NaN` sign. - /// - /// Example: - /// ```motoko include=import no-validate - /// assert Float.format(#exp 3, 123.0) == "1.230e+02"; - /// ``` - public func format(self : Float, fmt : { #fix : Nat8; #exp : Nat8; #gen : Nat8; #exact }) : Text = switch fmt { - case (#fix(prec)) { Prim.floatToFormattedText(self, prec, 0) }; - case (#exp(prec)) { Prim.floatToFormattedText(self, prec, 1) }; - case (#gen(prec)) { Prim.floatToFormattedText(self, prec, 2) }; - case (#exact) { Prim.floatToFormattedText(self, 17, 2) } - }; - - /// Conversion to Text. Use `format(fmt, x)` for more detailed control. - /// - /// `-0.0` is formatted with negative sign bit. - /// Positive infinity is formatted as `inf`. - /// Negative infinity is formatted as `-inf`. - /// `NaN` is formatted as `NaN` or `-NaN` depending on its sign bit. - /// - /// The numerical precision and the text format can vary between - /// Motoko versions and runtime configuration. Moreover, `NaN` can be printed - /// differently, i.e. "NaN" or "nan", potentially omitting the `NaN` sign. - /// - /// Example: - /// ```motoko include=import no-validate - /// assert Float.toText(1.2) == "1.2"; - /// ``` - public let toText : (self : Float) -> Text = Prim.floatToText; - - /// Conversion to Int64 by truncating Float, equivalent to `toInt64(trunc(f))` - /// - /// Traps if the floating point number is larger or smaller than the representable Int64. - /// Also traps for `inf`, `-inf`, and `NaN`. - /// - /// Example: - /// ```motoko include=import - /// assert Float.toInt64(-12.3) == -12; - /// ``` - public let toInt64 : (self : Float) -> Int64 = Prim.floatToInt64; - - /// Conversion from Int64. - /// - /// Note: The floating point number may be imprecise for large or small Int64. - /// - /// Example: - /// ```motoko include=import - /// assert Float.fromInt64(-42) == -42.0; - /// ``` - public let fromInt64 : (x : Int64) -> Float = Prim.int64ToFloat; - - /// Conversion to Int. - /// - /// Traps for `inf`, `-inf`, and `NaN`. - /// - /// Example: - /// ```motoko include=import - /// assert Float.toInt(1.2e6) == +1_200_000; - /// ``` - public let toInt : (self : Float) -> Int = Prim.floatToInt; - - /// Conversion from Int. May result in `Inf`. - /// - /// Note: The floating point number may be imprecise for large or small Int values. - /// Returns `inf` if the integer is greater than the maximum floating point number. - /// Returns `-inf` if the integer is less than the minimum floating point number. - /// - /// Example: - /// ```motoko include=import - /// assert Float.fromInt(-123) == -123.0; - /// ``` - /// @deprecated M0235 - public let fromInt : (x : Int) -> Float = Prim.intToFloat; - - /// Conversion to Float32 (32-bit single precision). - /// - /// Note: This may lose precision for values that are not exactly representable in 32-bit. - /// - /// Example: - /// ```motoko include=import - /// assert Float.toFloat32(1.5) == 1.5; - /// ``` - public let toFloat32 : (self : Float) -> Prim.Types.Float32 = Prim.floatToFloat32; - - /// Conversion from Float32 (32-bit single precision) to Float (64-bit double precision). - /// - /// This is a lossless widening conversion. - /// - /// Example: - /// ```motoko include=import - /// assert Float.fromFloat32(1.5) == 1.5; - /// ``` - public let fromFloat32 : (x : Prim.Types.Float32) -> Float = Prim.float32ToFloat; - - /// Determines whether `x` is equal to `y` within the defined tolerance of `epsilon`. - /// The `epsilon` considers numerical erros, see comment above. - /// Equivalent to `Float.abs(x - y) <= epsilon` for a non-negative epsilon. - /// - /// Traps if `epsilon` is negative or `NaN`. - /// - /// Special cases: - /// ``` - /// equal(+0.0, -0.0, epsilon) => true for any `epsilon >= 0.0` - /// equal(-0.0, +0.0, epsilon) => true for any `epsilon >= 0.0` - /// equal(+inf, +inf, epsilon) => true for any `epsilon >= 0.0` - /// equal(-inf, -inf, epsilon) => true for any `epsilon >= 0.0` - /// equal(x, NaN, epsilon) => false for any x and `epsilon >= 0.0` - /// equal(NaN, y, epsilon) => false for any y and `epsilon >= 0.0` - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(-12.3, -1.23e1, epsilon); - /// ``` - public func equal(x : Float, y : Float, epsilon : Float) : Bool { - if (not (epsilon >= 0.0)) { - // also considers NaN, not identical to `epsilon < 0.0` - Prim.trap("Float.equal(): epsilon must be greater or equal 0.0") - }; - x == y or abs(x - y) <= epsilon // `x == y` to also consider infinity equal - }; - - /// Determines whether `x` is not equal to `y` within the defined tolerance of `epsilon`. - /// The `epsilon` considers numerical erros, see comment above. - /// Equivalent to `not equal(x, y, epsilon)`. - /// - /// Traps if `epsilon` is negative or `NaN`. - /// - /// Special cases: - /// ``` - /// notEqual(+0.0, -0.0, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(-0.0, +0.0, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(+inf, +inf, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(-inf, -inf, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(x, NaN, epsilon) => true for any x and `epsilon >= 0.0` - /// notEqual(NaN, y, epsilon) => true for any y and `epsilon >= 0.0` - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert not Float.notEqual(-12.3, -1.23e1, epsilon); - /// ``` - public func notEqual(x : Float, y : Float, epsilon : Float) : Bool { - if (not (epsilon >= 0.0)) { - // also considers NaN, not identical to `epsilon < 0.0` - Prim.trap("Float.notEqual(): epsilon must be greater or equal 0.0") - }; - not (x == y or abs(x - y) <= epsilon) - }; - - /// Returns `x < y`. - /// - /// Special cases: - /// ``` - /// less(+0.0, -0.0) => false - /// less(-0.0, +0.0) => false - /// less(NaN, y) => false for any Float y - /// less(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.less(Float.e, Float.pi); - /// ``` - public func less(x : Float, y : Float) : Bool { x < y }; - - /// Returns `x <= y`. - /// - /// Special cases: - /// ``` - /// lessOrEqual(+0.0, -0.0) => true - /// lessOrEqual(-0.0, +0.0) => true - /// lessOrEqual(NaN, y) => false for any Float y - /// lessOrEqual(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.lessOrEqual(0.123, 0.1234); - /// ``` - public func lessOrEqual(x : Float, y : Float) : Bool { x <= y }; - - /// Returns `x > y`. - /// - /// Special cases: - /// ``` - /// greater(+0.0, -0.0) => false - /// greater(-0.0, +0.0) => false - /// greater(NaN, y) => false for any Float y - /// greater(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.greater(Float.pi, Float.e); - /// ``` - public func greater(x : Float, y : Float) : Bool { x > y }; - - /// Returns `x >= y`. - /// - /// Special cases: - /// ``` - /// greaterOrEqual(+0.0, -0.0) => true - /// greaterOrEqual(-0.0, +0.0) => true - /// greaterOrEqual(NaN, y) => false for any Float y - /// greaterOrEqual(x, NaN) => false for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float.greaterOrEqual(0.1234, 0.123); - /// ``` - public func greaterOrEqual(x : Float, y : Float) : Bool { - x >= y - }; - - /// Defines a total order of `x` and `y` for use in sorting. - /// - /// Note: Using this operation to determine equality or inequality is discouraged for two reasons: - /// * It does not consider numerical errors, see comment above. Use `equal(x, y, espilon)` or - /// `notEqual(x, y, epsilon)` to test for equality or inequality, respectively. - /// * `NaN` are here considered equal if their sign matches, which is different to the standard equality - /// by `==` or when using `equal()` or `notEqual()`. - /// - /// Total order: - /// * negative NaN (no distinction between signalling and quiet negative NaN) - /// * negative infinity - /// * negative numbers (including negative subnormal numbers in standard order) - /// * negative zero (`-0.0`) - /// * positive zero (`+0.0`) - /// * positive numbers (including positive subnormal numbers in standard order) - /// * positive infinity - /// * positive NaN (no distinction between signalling and quiet positive NaN) - /// - /// Example: - /// ```motoko include=import - /// assert Float.compare(0.123, 0.1234) == #less; - /// ``` - public func compare(x : Float, y : Float) : Order.Order { - if (isNaN(x)) { - if (isNegative(x)) { - if (isNaN(y) and isNegative(y)) { #equal } else { #less } - } else { - if (isNaN(y) and not isNegative(y)) { #equal } else { #greater } - } - } else if (isNaN(y)) { - if (isNegative(y)) { - #greater - } else { - #less - } - } else { - if (x == y) { #equal } else if (x < y) { #less } else { - #greater - } - } - }; - - func isNegative(self : Float) : Bool { - copySign(1.0, self) < 0.0 - }; - - /// Returns the negation of `x`, `-x` . - /// - /// Changes the sign bit for infinity. - /// - /// Special cases: - /// ``` - /// neg(+inf) => -inf - /// neg(-inf) => +inf - /// neg(+NaN) => -NaN - /// neg(-NaN) => +NaN - /// neg(+0.0) => -0.0 - /// neg(-0.0) => +0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.neg(1.23), -1.23, epsilon); - /// ``` - public func neg(x : Float) : Float { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// add(+inf, y) => +inf if y is any Float except -inf and NaN - /// add(-inf, y) => -inf if y is any Float except +inf and NaN - /// add(+inf, -inf) => NaN - /// add(NaN, y) => NaN for any Float y - /// ``` - /// The same cases apply commutatively, i.e. for `add(y, x)`. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.add(1.23, 0.123), 1.353, epsilon); - /// ``` - public func add(x : Float, y : Float) : Float { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// sub(+inf, y) => +inf if y is any Float except +inf or NaN - /// sub(-inf, y) => -inf if y is any Float except -inf and NaN - /// sub(x, +inf) => -inf if x is any Float except +inf and NaN - /// sub(x, -inf) => +inf if x is any Float except -inf and NaN - /// sub(+inf, +inf) => NaN - /// sub(-inf, -inf) => NaN - /// sub(NaN, y) => NaN for any Float y - /// sub(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.sub(1.23, 0.123), 1.107, epsilon); - /// ``` - public func sub(x : Float, y : Float) : Float { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// mul(+inf, y) => +inf if y > 0.0 - /// mul(-inf, y) => -inf if y > 0.0 - /// mul(+inf, y) => -inf if y < 0.0 - /// mul(-inf, y) => +inf if y < 0.0 - /// mul(+inf, 0.0) => NaN - /// mul(-inf, 0.0) => NaN - /// mul(NaN, y) => NaN for any Float y - /// ``` - /// The same cases apply commutatively, i.e. for `mul(y, x)`. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.mul(1.23, 1e2), 123.0, epsilon); - /// ``` - public func mul(x : Float, y : Float) : Float { x * y }; - - /// Returns the division of `x` by `y`, `x / y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// div(0.0, 0.0) => NaN - /// div(x, 0.0) => +inf for x > 0.0 - /// div(x, 0.0) => -inf for x < 0.0 - /// div(x, +inf) => 0.0 for any x except +inf, -inf, and NaN - /// div(x, -inf) => 0.0 for any x except +inf, -inf, and NaN - /// div(+inf, y) => +inf if y >= 0.0 - /// div(+inf, y) => -inf if y < 0.0 - /// div(-inf, y) => -inf if y >= 0.0 - /// div(-inf, y) => +inf if y < 0.0 - /// div(NaN, y) => NaN for any Float y - /// div(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.div(1.23, 1e2), 0.0123, epsilon); - /// ``` - public func div(x : Float, y : Float) : Float { x / y }; - - /// Returns the floating point division remainder `x % y`, - /// which is defined as `x - trunc(x / y) * y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// rem(0.0, 0.0) => NaN - /// rem(x, y) => +inf if sign(x) == sign(y) for any x and y not being +inf, -inf, or NaN - /// rem(x, y) => -inf if sign(x) != sign(y) for any x and y not being +inf, -inf, or NaN - /// rem(x, +inf) => x for any x except +inf, -inf, and NaN - /// rem(x, -inf) => x for any x except +inf, -inf, and NaN - /// rem(+inf, y) => NaN for any Float y - /// rem(-inf, y) => NaN for any Float y - /// rem(NaN, y) => NaN for any Float y - /// rem(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.rem(7.2, 2.3), 0.3, epsilon); - /// ``` - public func rem(x : Float, y : Float) : Float { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// pow(+inf, y) => +inf for any y > 0.0 including +inf - /// pow(+inf, 0.0) => 1.0 - /// pow(+inf, y) => 0.0 for any y < 0.0 including -inf - /// pow(x, +inf) => +inf if x > 0.0 or x < 0.0 - /// pow(0.0, +inf) => 0.0 - /// pow(x, -inf) => 0.0 if x > 0.0 or x < 0.0 - /// pow(0.0, -inf) => +inf - /// pow(x, y) => NaN if x < 0.0 and y is a non-integral Float - /// pow(-inf, y) => +inf if y > 0.0 and y is a non-integral or an even integral Float - /// pow(-inf, y) => -inf if y > 0.0 and y is an odd integral Float - /// pow(-inf, 0.0) => 1.0 - /// pow(-inf, y) => 0.0 if y < 0.0 - /// pow(-inf, +inf) => +inf - /// pow(-inf, -inf) => 1.0 - /// pow(NaN, y) => NaN if y != 0.0 - /// pow(NaN, 0.0) => 1.0 - /// pow(x, NaN) => NaN for any Float x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-6; - /// assert Float.equal(Float.pow(2.5, 2.0), 6.25, epsilon); - /// ``` - public func pow(x : Float, y : Float) : Float { x ** y }; - -} diff --git a/.mops/core@2.5.0/src/Float32.mo b/.mops/core@2.5.0/src/Float32.mo deleted file mode 100644 index d0ac89f..0000000 --- a/.mops/core@2.5.0/src/Float32.mo +++ /dev/null @@ -1,850 +0,0 @@ -/// Single precision (32-bit) floating-point numbers in IEEE 754 representation. -/// -/// This module contains common floating-point constants and utility functions. -/// -/// ```motoko name=import -/// import Float32 "mo:core/Float32"; -/// ``` -/// -/// Notation for special values in the documentation below: -/// `+inf`: Positive infinity -/// `-inf`: Negative infinity -/// `NaN`: "not a number" (can have different sign bit values, but `NaN != NaN` regardless of the sign). -/// -/// Note: -/// Floating point numbers have limited precision and operations may inherently result in numerical errors. -/// `Float32` has less precision than `Float` (64-bit); only about 7 significant decimal digits. -/// -/// Examples of numerical errors: -/// ```motoko -/// assert 0.1 + 0.1 + 0.1 != 0.3; -/// ``` -/// -/// Advice: -/// * Floating point number comparisons by `==` or `!=` are discouraged. Instead, it is better to compare -/// floating-point numbers with a numerical tolerance, called epsilon. -/// -/// Example: -/// ```motoko -/// import Float32 "mo:core/Float32"; -/// let x = 0.1 + 0.1 + 0.1 : Float32; -/// let y = 0.3 : Float32; -/// -/// let epsilon = 1e-5 : Float32; // This depends on the application case (needs a numerical error analysis). -/// assert Float32.equal(x, y, epsilon); -/// ``` -/// -/// * For absolute precision, it is recommended to encode the fraction number as a pair of a Nat for the base -/// and a Nat for the exponent (decimal point). -/// -/// Note: As of `moc` 1.4, `Float32` support is experimental. -/// -/// NaN sign: -/// * The NaN sign is only applied by `abs`, `neg`, and `copySign`. Other operations can have an arbitrary -/// sign bit for NaN results. - -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 32-bit floating point number type. - public type Float32 = Prim.Types.Float32; - - /// Conversion to Float (64-bit double precision). - /// - /// This is a lossless widening conversion. - /// - /// Example: - /// ```motoko include=import - /// assert Float32.toFloat(1.5) == 1.5; - /// ``` - public let toFloat : (self : Float32) -> Float = Prim.float32ToFloat; - - /// Conversion from Float (64-bit double precision) to Float32. - /// - /// Note: This may lose precision for values that are not exactly representable in 32-bit. - /// - /// Example: - /// ```motoko include=import - /// assert Float32.fromFloat(1.5) == 1.5; - /// ``` - public let fromFloat : (x : Float) -> Float32 = Prim.floatToFloat32; - - /// Ratio of the circumference of a circle to its diameter. - /// Note: Limited precision (approximately 7 significant decimal digits). - public let pi : Float32 = 3.14159265358979323846; - - /// Base of the natural logarithm. - /// Note: Limited precision (approximately 7 significant decimal digits). - public let e : Float32 = 2.7182818284590452354; - - /// Determines whether the `number` is a `NaN` ("not a number" in the floating point representation). - /// Notes: - /// * Equality test of `NaN` with itself or another number is always `false`. - /// * There exist many internal `NaN` value representations, such as positive and negative NaN, - /// signalling and quiet NaNs, each with many different bit representations. - /// - /// Example: - /// ```motoko include=import - /// assert Float32.isNaN(0.0/0.0); - /// ``` - public func isNaN(self : Float32) : Bool { - self != self - }; - - /// Returns the absolute value of `x`. - /// - /// Special cases: - /// ``` - /// abs(+inf) => +inf - /// abs(-inf) => +inf - /// abs(-NaN) => +NaN - /// abs(-0.0) => 0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.abs(-1.2), 1.2, epsilon); - /// ``` - public func abs(x : Float32) : Float32 { - fromFloat(Prim.floatAbs(toFloat(x))) - }; - - /// Returns the square root of `x`. - /// - /// Special cases: - /// ``` - /// sqrt(+inf) => +inf - /// sqrt(-0.0) => -0.0 - /// sqrt(x) => NaN if x < 0.0 - /// sqrt(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.sqrt(6.25), 2.5, epsilon); - /// ``` - public func sqrt(x : Float32) : Float32 { - fromFloat(Prim.floatSqrt(toFloat(x))) - }; - - /// Returns the smallest integral float greater than or equal to `x`. - /// - /// Special cases: - /// ``` - /// ceil(+inf) => +inf - /// ceil(-inf) => -inf - /// ceil(NaN) => NaN - /// ceil(0.0) => 0.0 - /// ceil(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.ceil(1.2), 2.0, epsilon); - /// ``` - public func ceil(x : Float32) : Float32 { - fromFloat(Prim.floatCeil(toFloat(x))) - }; - - /// Returns the largest integral float less than or equal to `x`. - /// - /// Special cases: - /// ``` - /// floor(+inf) => +inf - /// floor(-inf) => -inf - /// floor(NaN) => NaN - /// floor(0.0) => 0.0 - /// floor(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.floor(1.2), 1.0, epsilon); - /// ``` - public func floor(x : Float32) : Float32 { - fromFloat(Prim.floatFloor(toFloat(x))) - }; - - /// Returns the nearest integral float not greater in magnitude than `x`. - /// This is equivalent to returning `x` with truncating its decimal places. - /// - /// Special cases: - /// ``` - /// trunc(+inf) => +inf - /// trunc(-inf) => -inf - /// trunc(NaN) => NaN - /// trunc(0.0) => 0.0 - /// trunc(-0.0) => -0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.trunc(2.75), 2.0, epsilon); - /// ``` - public func trunc(x : Float32) : Float32 { - fromFloat(Prim.floatTrunc(toFloat(x))) - }; - - /// Returns the nearest integral float to `x`. - /// A decimal place of exactly .5 is rounded to the nearest even integral float. - /// - /// Special cases: - /// ``` - /// nearest(+inf) => +inf - /// nearest(-inf) => -inf - /// nearest(NaN) => NaN - /// nearest(0.0) => 0.0 - /// nearest(-0.0) => -0.0 - /// nearest(14.5) => 14.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.nearest(2.75) == 3.0 - /// ``` - public func nearest(x : Float32) : Float32 { - fromFloat(Prim.floatNearest(toFloat(x))) - }; - - /// Returns `x` if `x` and `y` have same sign, otherwise `x` with negated sign. - /// - /// The sign bit of zero, infinity, and `NaN` is considered. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.copySign(1.2, -2.3), -1.2, epsilon); - /// ``` - public func copySign(x : Float32, y : Float32) : Float32 { - fromFloat(Prim.floatCopySign(toFloat(x), toFloat(y))) - }; - - /// Returns the smaller value of `x` and `y`. - /// - /// Special cases: - /// ``` - /// min(NaN, y) => NaN for any Float32 y - /// min(x, NaN) => NaN for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.min(1.2, -2.3) == -2.3; // with numerical imprecision - /// ``` - public func min(x : Float32, y : Float32) : Float32 { - fromFloat(Prim.floatMin(toFloat(x), toFloat(y))) - }; - - /// Returns the larger value of `x` and `y`. - /// - /// Special cases: - /// ``` - /// max(NaN, y) => NaN for any Float32 y - /// max(x, NaN) => NaN for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.max(1.2, -2.3) == 1.2; - /// ``` - public func max(x : Float32, y : Float32) : Float32 { - fromFloat(Prim.floatMax(toFloat(x), toFloat(y))) - }; - - /// Returns the sine of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// sin(+inf) => NaN - /// sin(-inf) => NaN - /// sin(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.sin(Float32.pi / 2.0), 1.0, epsilon); - /// ``` - public func sin(x : Float32) : Float32 { - fromFloat(Prim.sin(toFloat(x))) - }; - - /// Returns the cosine of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// cos(+inf) => NaN - /// cos(-inf) => NaN - /// cos(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.cos(Float32.pi / 2.0), 0.0, epsilon); - /// ``` - public func cos(x : Float32) : Float32 { - fromFloat(Prim.cos(toFloat(x))) - }; - - /// Returns the tangent of the radian angle `x`. - /// - /// Special cases: - /// ``` - /// tan(+inf) => NaN - /// tan(-inf) => NaN - /// tan(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.tan(Float32.pi / 4.0), 1.0, epsilon); - /// ``` - public func tan(x : Float32) : Float32 { - fromFloat(Prim.tan(toFloat(x))) - }; - - /// Returns the arc sine of `x` in radians. - /// - /// Special cases: - /// ``` - /// arcsin(x) => NaN if x > 1.0 - /// arcsin(x) => NaN if x < -1.0 - /// arcsin(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.arcsin(1.0), Float32.pi / 2.0, epsilon); - /// ``` - public func arcsin(x : Float32) : Float32 { - fromFloat(Prim.arcsin(toFloat(x))) - }; - - /// Returns the arc cosine of `x` in radians. - /// - /// Special cases: - /// ``` - /// arccos(x) => NaN if x > 1.0 - /// arccos(x) => NaN if x < -1.0 - /// arccos(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.arccos(1.0), 0.0, epsilon); - /// ``` - public func arccos(x : Float32) : Float32 { - fromFloat(Prim.arccos(toFloat(x))) - }; - - /// Returns the arc tangent of `x` in radians. - /// - /// Special cases: - /// ``` - /// arctan(+inf) => pi / 2 - /// arctan(-inf) => -pi / 2 - /// arctan(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.arctan(1.0), Float32.pi / 4.0, epsilon); - /// ``` - public func arctan(x : Float32) : Float32 { - fromFloat(Prim.arctan(toFloat(x))) - }; - - /// Given `(y, x)`, returns the arc tangent in radians of `y/x` based on the signs of both values to determine the correct quadrant. - /// - /// Special cases: - /// ``` - /// arctan2(0.0, 0.0) => 0.0 - /// arctan2(-0.0, 0.0) => -0.0 - /// arctan2(0.0, -0.0) => pi - /// arctan2(-0.0, -0.0) => -pi - /// arctan2(+inf, +inf) => pi / 4 - /// arctan2(+inf, -inf) => 3 * pi / 4 - /// arctan2(-inf, +inf) => -pi / 4 - /// arctan2(-inf, -inf) => -3 * pi / 4 - /// arctan2(NaN, x) => NaN for any Float32 x - /// arctan2(y, NaN) => NaN for any Float32 y - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let sqrt2over2 = Float32.sqrt(2.0) / 2.0; - /// assert Float32.arctan2(sqrt2over2, sqrt2over2) == Float32.pi / 4.0; - /// ``` - public func arctan2(y : Float32, x : Float32) : Float32 { - fromFloat(Prim.arctan2(toFloat(y), toFloat(x))) - }; - - /// Returns the value of `e` raised to the `x`-th power. - /// - /// Special cases: - /// ``` - /// exp(+inf) => +inf - /// exp(-inf) => 0.0 - /// exp(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.exp(1.0), Float32.e, epsilon); - /// ``` - public func exp(x : Float32) : Float32 { - fromFloat(Prim.exp(toFloat(x))) - }; - - /// Returns the natural logarithm (base-`e`) of `x`. - /// - /// Special cases: - /// ``` - /// log(0.0) => -inf - /// log(-0.0) => -inf - /// log(x) => NaN if x < 0.0 - /// log(+inf) => +inf - /// log(NaN) => NaN - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.log(Float32.e), 1.0, epsilon); - /// ``` - public func log(x : Float32) : Float32 { - fromFloat(Prim.log(toFloat(x))) - }; - - /// Formatting. `format(fmt, x)` formats `x` to `Text` according to the - /// formatting directive `fmt`, which can take one of the following forms: - /// - /// * `#fix prec` as fixed-point format with `prec` digits - /// * `#exp prec` as exponential format with `prec` digits - /// * `#gen prec` as generic format with `prec` digits - /// * `#exact` as exact format that can be decoded without loss. - /// - /// `-0.0` is formatted with negative sign bit. - /// Positive infinity is formatted as "inf". - /// Negative infinity is formatted as "-inf". - /// - /// The numerical precision and the text format can vary between - /// Motoko versions and runtime configuration. Moreover, `NaN` can be printed - /// differently, i.e. "NaN" or "nan", potentially omitting the `NaN` sign. - /// - /// Example: - /// ```motoko include=import no-validate - /// assert Float32.format(123.0 : Float32, #exp (3 : Nat8)) == "1.230e+02"; - /// ``` - public func format(self : Float32, fmt : { #fix : Nat8; #exp : Nat8; #gen : Nat8; #exact }) : Text { - let f = toFloat(self); - switch fmt { - case (#fix(prec)) { Prim.floatToFormattedText(f, prec, 0) }; - case (#exp(prec)) { Prim.floatToFormattedText(f, prec, 1) }; - case (#gen(prec)) { Prim.floatToFormattedText(f, prec, 2) }; - case (#exact) { Prim.floatToFormattedText(f, 17, 2) } - } - }; - - /// Conversion to Text. Use `format(fmt, x)` for more detailed control. - /// - /// `-0.0` is formatted with negative sign bit. - /// Positive infinity is formatted as `inf`. - /// Negative infinity is formatted as `-inf`. - /// `NaN` is formatted as `NaN` or `-NaN` depending on its sign bit. - /// - /// The numerical precision and the text format can vary between - /// Motoko versions and runtime configuration. Moreover, `NaN` can be printed - /// differently, i.e. "NaN" or "nan", potentially omitting the `NaN` sign. - /// - /// Example: - /// ```motoko include=import no-validate - /// assert Float32.toText(1.5) == "1.5"; - /// ``` - public func toText(self : Float32) : Text { - Prim.floatToText(toFloat(self)) - }; - - /// Conversion to Int64 by truncating Float32, equivalent to `toInt64(trunc(f))` - /// - /// Traps if the floating point number is larger or smaller than the representable Int64. - /// Also traps for `inf`, `-inf`, and `NaN`. - /// - /// Example: - /// ```motoko include=import - /// assert Float32.toInt64(-12.0) == -12; - /// ``` - public func toInt64(self : Float32) : Int64 { - Prim.floatToInt64(toFloat(self)) - }; - - /// Conversion from Int64. - /// - /// Note: The floating point number may be imprecise for large or small Int64. - /// - /// Example: - /// ```motoko include=import - /// assert Float32.fromInt64(-42) == -42.0; - /// ``` - public func fromInt64(x : Int64) : Float32 { - fromFloat(Prim.int64ToFloat(x)) - }; - - /// Conversion to Int. - /// - /// Traps for `inf`, `-inf`, and `NaN`. - /// - /// Example: - /// ```motoko include=import - /// assert Float32.toInt(1.0e6) == +1_000_000; - /// ``` - public func toInt(self : Float32) : Int { - Prim.floatToInt(toFloat(self)) - }; - - /// Determines whether `x` is equal to `y` within the defined tolerance of `epsilon`. - /// The `epsilon` considers numerical errors, see comment above. - /// Equivalent to `Float32.abs(x - y) <= epsilon` for a non-negative epsilon. - /// - /// Traps if `epsilon` is negative or `NaN`. - /// - /// Special cases: - /// ``` - /// equal(+0.0, -0.0, epsilon) => true for any `epsilon >= 0.0` - /// equal(-0.0, +0.0, epsilon) => true for any `epsilon >= 0.0` - /// equal(+inf, +inf, epsilon) => true for any `epsilon >= 0.0` - /// equal(-inf, -inf, epsilon) => true for any `epsilon >= 0.0` - /// equal(x, NaN, epsilon) => false for any x and `epsilon >= 0.0` - /// equal(NaN, y, epsilon) => false for any y and `epsilon >= 0.0` - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(-12.3, -1.23e1, epsilon); - /// ``` - public func equal(x : Float32, y : Float32, epsilon : Float32) : Bool { - if (not (epsilon >= (0.0 : Float32))) { - // also considers NaN, not identical to `epsilon < 0.0` - Prim.trap("Float32.equal(): epsilon must be greater or equal 0.0") - }; - x == y or abs(x - y) <= epsilon // `x == y` to also consider infinity equal - }; - - /// Determines whether `x` is not equal to `y` within the defined tolerance of `epsilon`. - /// The `epsilon` considers numerical errors, see comment above. - /// Equivalent to `not equal(x, y, epsilon)`. - /// - /// Traps if `epsilon` is negative or `NaN`. - /// - /// Special cases: - /// ``` - /// notEqual(+0.0, -0.0, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(-0.0, +0.0, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(+inf, +inf, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(-inf, -inf, epsilon) => false for any `epsilon >= 0.0` - /// notEqual(x, NaN, epsilon) => true for any x and `epsilon >= 0.0` - /// notEqual(NaN, y, epsilon) => true for any y and `epsilon >= 0.0` - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert not Float32.notEqual(-12.3, -1.23e1, epsilon); - /// ``` - public func notEqual(x : Float32, y : Float32, epsilon : Float32) : Bool { - if (not (epsilon >= (0.0 : Float32))) { - // also considers NaN, not identical to `epsilon < 0.0` - Prim.trap("Float32.notEqual(): epsilon must be greater or equal 0.0") - }; - not (x == y or abs(x - y) <= epsilon) - }; - - /// Returns `x < y`. - /// - /// Special cases: - /// ``` - /// less(+0.0, -0.0) => false - /// less(-0.0, +0.0) => false - /// less(NaN, y) => false for any Float32 y - /// less(x, NaN) => false for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.less(Float32.e, Float32.pi); - /// ``` - public func less(x : Float32, y : Float32) : Bool { x < y }; - - /// Returns `x <= y`. - /// - /// Special cases: - /// ``` - /// lessOrEqual(+0.0, -0.0) => true - /// lessOrEqual(-0.0, +0.0) => true - /// lessOrEqual(NaN, y) => false for any Float32 y - /// lessOrEqual(x, NaN) => false for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.lessOrEqual(0.123, 0.1234); - /// ``` - public func lessOrEqual(x : Float32, y : Float32) : Bool { x <= y }; - - /// Returns `x > y`. - /// - /// Special cases: - /// ``` - /// greater(+0.0, -0.0) => false - /// greater(-0.0, +0.0) => false - /// greater(NaN, y) => false for any Float32 y - /// greater(x, NaN) => false for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.greater(Float32.pi, Float32.e); - /// ``` - public func greater(x : Float32, y : Float32) : Bool { x > y }; - - /// Returns `x >= y`. - /// - /// Special cases: - /// ``` - /// greaterOrEqual(+0.0, -0.0) => true - /// greaterOrEqual(-0.0, +0.0) => true - /// greaterOrEqual(NaN, y) => false for any Float32 y - /// greaterOrEqual(x, NaN) => false for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// assert Float32.greaterOrEqual(0.1234, 0.123); - /// ``` - public func greaterOrEqual(x : Float32, y : Float32) : Bool { - x >= y - }; - - /// Defines a total order of `x` and `y` for use in sorting. - /// - /// Note: Using this operation to determine equality or inequality is discouraged for two reasons: - /// * It does not consider numerical errors, see comment above. Use `equal(x, y, epsilon)` or - /// `notEqual(x, y, epsilon)` to test for equality or inequality, respectively. - /// * `NaN` are here considered equal if their sign matches, which is different to the standard equality - /// by `==` or when using `equal()` or `notEqual()`. - /// - /// Total order: - /// * negative NaN (no distinction between signalling and quiet negative NaN) - /// * negative infinity - /// * negative numbers (including negative subnormal numbers in standard order) - /// * negative zero (`-0.0`) - /// * positive zero (`+0.0`) - /// * positive numbers (including positive subnormal numbers in standard order) - /// * positive infinity - /// * positive NaN (no distinction between signalling and quiet positive NaN) - /// - /// Example: - /// ```motoko include=import - /// assert Float32.compare(0.123, 0.1234) == #less; - /// ``` - public func compare(x : Float32, y : Float32) : Order.Order { - if (isNaN(x)) { - if (isNegative(x)) { - if (isNaN(y) and isNegative(y)) { #equal } else { #less } - } else { - if (isNaN(y) and not isNegative(y)) { #equal } else { #greater } - } - } else if (isNaN(y)) { - if (isNegative(y)) { - #greater - } else { - #less - } - } else { - if (x == y) { #equal } else if (x < y) { #less } else { - #greater - } - } - }; - - func isNegative(self : Float32) : Bool { - copySign(1.0, self) < (0.0 : Float32) - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Changes the sign bit for infinity. - /// - /// Special cases: - /// ``` - /// neg(+inf) => -inf - /// neg(-inf) => +inf - /// neg(+NaN) => -NaN - /// neg(-NaN) => +NaN - /// neg(+0.0) => -0.0 - /// neg(-0.0) => +0.0 - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.neg(1.23), -1.23, epsilon); - /// ``` - public func neg(x : Float32) : Float32 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// add(+inf, y) => +inf if y is any Float32 except -inf and NaN - /// add(-inf, y) => -inf if y is any Float32 except +inf and NaN - /// add(+inf, -inf) => NaN - /// add(NaN, y) => NaN for any Float32 y - /// ``` - /// The same cases apply commutatively, i.e. for `add(y, x)`. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.add(1.23, 0.123), 1.353, epsilon); - /// ``` - public func add(x : Float32, y : Float32) : Float32 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// sub(+inf, y) => +inf if y is any Float32 except +inf or NaN - /// sub(-inf, y) => -inf if y is any Float32 except -inf and NaN - /// sub(x, +inf) => -inf if x is any Float32 except +inf and NaN - /// sub(x, -inf) => +inf if x is any Float32 except -inf and NaN - /// sub(+inf, +inf) => NaN - /// sub(-inf, -inf) => NaN - /// sub(NaN, y) => NaN for any Float32 y - /// sub(x, NaN) => NaN for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.sub(1.23, 0.123), 1.107, epsilon); - /// ``` - public func sub(x : Float32, y : Float32) : Float32 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// mul(+inf, y) => +inf if y > 0.0 - /// mul(-inf, y) => -inf if y > 0.0 - /// mul(+inf, y) => -inf if y < 0.0 - /// mul(-inf, y) => +inf if y < 0.0 - /// mul(+inf, 0.0) => NaN - /// mul(-inf, 0.0) => NaN - /// mul(NaN, y) => NaN for any Float32 y - /// ``` - /// The same cases apply commutatively, i.e. for `mul(y, x)`. - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.mul(1.23, 1e2), 123.0, epsilon); - /// ``` - public func mul(x : Float32, y : Float32) : Float32 { x * y }; - - /// Returns the division of `x` by `y`, `x / y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// div(0.0, 0.0) => NaN - /// div(x, 0.0) => +inf for x > 0.0 - /// div(x, 0.0) => -inf for x < 0.0 - /// div(x, +inf) => 0.0 for any x except +inf, -inf, and NaN - /// div(x, -inf) => 0.0 for any x except +inf, -inf, and NaN - /// div(+inf, y) => +inf if y >= 0.0 - /// div(+inf, y) => -inf if y < 0.0 - /// div(-inf, y) => -inf if y >= 0.0 - /// div(-inf, y) => +inf if y < 0.0 - /// div(NaN, y) => NaN for any Float32 y - /// div(x, NaN) => NaN for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.div(1.23, 1e2), 0.0123, epsilon); - /// ``` - public func div(x : Float32, y : Float32) : Float32 { x / y }; - - /// Returns the floating point division remainder `x % y`, - /// which is defined as `x - trunc(x / y) * y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// rem(0.0, 0.0) => NaN - /// rem(x, +inf) => x for any x except +inf, -inf, and NaN - /// rem(x, -inf) => x for any x except +inf, -inf, and NaN - /// rem(+inf, y) => NaN for any Float32 y - /// rem(-inf, y) => NaN for any Float32 y - /// rem(NaN, y) => NaN for any Float32 y - /// rem(x, NaN) => NaN for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.rem(7.2, 2.3), 0.3, epsilon); - /// ``` - public func rem(x : Float32, y : Float32) : Float32 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Note: Numerical errors may occur, see comment above. - /// - /// Special cases: - /// ``` - /// pow(+inf, y) => +inf for any y > 0.0 including +inf - /// pow(+inf, 0.0) => 1.0 - /// pow(+inf, y) => 0.0 for any y < 0.0 including -inf - /// pow(x, +inf) => +inf if x > 0.0 or x < 0.0 - /// pow(0.0, +inf) => 0.0 - /// pow(x, -inf) => 0.0 if x > 0.0 or x < 0.0 - /// pow(0.0, -inf) => +inf - /// pow(x, y) => NaN if x < 0.0 and y is a non-integral Float32 - /// pow(NaN, y) => NaN if y != 0.0 - /// pow(NaN, 0.0) => 1.0 - /// pow(x, NaN) => NaN for any Float32 x - /// ``` - /// - /// Example: - /// ```motoko include=import - /// let epsilon = 1e-5 : Float32; - /// assert Float32.equal(Float32.pow(2.5, 2.0), 6.25, epsilon); - /// ``` - public func pow(x : Float32, y : Float32) : Float32 { x ** y }; - -} diff --git a/.mops/core@2.5.0/src/Func.mo b/.mops/core@2.5.0/src/Func.mo deleted file mode 100644 index e2bb10c..0000000 --- a/.mops/core@2.5.0/src/Func.mo +++ /dev/null @@ -1,48 +0,0 @@ -/// Functions on functions, creating functions from simpler inputs. -/// -/// (Most commonly used when programming in functional style using higher-order -/// functions.) -/// -/// Import from the core package to use this module. -/// -/// ```motoko name=import -/// import Func = "mo:core/Func"; -/// ``` - -module { - - /// The composition of two functions `f` and `g` is a function that applies `g` and then `f`. - /// - /// Example: - /// ```motoko include=import - /// import Text "mo:core/Text"; - /// import Char "mo:core/Char"; - /// - /// let textFromNat32 = Func.compose(Text.fromChar, Char.fromNat32); - /// assert textFromNat32(65) == "A"; - /// ``` - public func compose(f : B -> C, g : A -> B) : A -> C { - func(x : A) : C { - f(g(x)) - } - }; - - /// The `identity` function returns its argument. - /// Example: - /// ```motoko include=import - /// assert Func.identity(10) == 10; - /// assert Func.identity(true) == true; - /// ``` - public func identity(x : A) : A = x; - - /// The const function is a _curried_ function that accepts an argument `x`, - /// and then returns a function that discards its argument and always returns - /// the `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Func.const(10)("hello") == 10; - /// assert Func.const(true)(20) == true; - /// ``` - public func const(x : A) : B -> A = func _ = x -} diff --git a/.mops/core@2.5.0/src/Int.mo b/.mops/core@2.5.0/src/Int.mo deleted file mode 100644 index 37ea9b7..0000000 --- a/.mops/core@2.5.0/src/Int.mo +++ /dev/null @@ -1,677 +0,0 @@ -/// Signed integer numbers with infinite precision (also called big integers). -/// -/// Most operations on integer numbers (e.g. addition) are available as built-in operators (e.g. `-1 + 1`). -/// This module provides equivalent functions and `Text` conversion. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int "mo:core/Int"; -/// ``` - -import Prim "mo:⛔"; -import Char "Char"; -import Runtime "Runtime"; -import Iter "Iter"; -import Order "Order"; - -module { - - /// Infinite precision signed integers. - public type Int = Prim.Types.Int; - - /// Returns the absolute value of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.abs(-12) == 12; - /// ``` - public let abs : (x : Int) -> Nat = Prim.abs; - - /// Converts an integer number to its textual representation. Textual - /// representation _do not_ contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toText(-1234) == "-1234"; - /// ``` - public func toText(self : Int) : Text { - if (self == 0) { - return "0" - }; - - let isNegative = self < 0; - var int = if isNegative { -self } else { self }; - - var text = ""; - let base = 10; - - while (int > 0) { - let rem = int % base; - text := ( - switch (rem) { - case 0 { "0" }; - case 1 { "1" }; - case 2 { "2" }; - case 3 { "3" }; - case 4 { "4" }; - case 5 { "5" }; - case 6 { "6" }; - case 7 { "7" }; - case 8 { "8" }; - case 9 { "9" }; - case _ { Runtime.unreachable() } - } - ) # text; - int := int / base - }; - - return if isNegative { "-" # text } else { text } - }; - - /// Creates a integer from its textual representation. Returns `null` - /// if the input is not a valid integer. - /// - /// The textual representation _must not_ contain underscores but may - /// begin with a '+' or '-' character. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromText("-1234") == ?-1234; - /// ``` - public func fromText(text : Text) : ?Int { - if (text == "") { - return null - }; - var n = 0; - var isFirst = true; - var isNegative = false; - var hasDigits = false; - for (c in text.chars()) { - if (isFirst and c == '+') { - // Skip character - } else if (isFirst and c == '-') { - isNegative := true - } else if (Char.isDigit(c)) { - hasDigits := true; - let charAsNat = Prim.nat32ToNat(Prim.charToNat32(c) -% Prim.charToNat32('0')); - n := n * 10 + charAsNat - } else { - return null - }; - isFirst := false - }; - if (not hasDigits) { - return null - }; - ?(if (isNegative) { -n } else { n }) - }; - - /// Creates a integer from its textual representation. Returns `null` - /// if the input is not a valid integer. - /// - /// This functions is meant to be used with contextual-dot notation. - /// - /// Example: - /// ```motoko include=import - /// assert "-1234".toInt() == ?-1234; - /// ``` - public func toInt(self : Text) : ?Int { - fromText(self) - }; - - /// Converts an integer to a natural number. Traps if the integer is negative. - /// - /// Example: - /// ```motoko include=import - /// import Debug "mo:core/Debug"; - /// assert Int.toNat(1234 : Int) == (1234 : Nat); - /// ``` - public func toNat(self : Int) : Nat { - if (self < 0) { - Runtime.trap("Int.toNat(): negative input value") - } else { - abs(self) - } - }; - - /// Converts a natural number to an integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromNat(1234 : Nat) == (1234 : Int); - /// ``` - public func fromNat(nat : Nat) : Int { - nat : Int - }; - - /// Conversion to Float. May result in `Inf`. - /// - /// Note: The floating point number may be imprecise for large or small Int values. - /// Returns `inf` if the integer is greater than the maximum floating point number. - /// Returns `-inf` if the integer is less than the minimum floating point number. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toFloat(-123) == -123.0; - /// ``` - public let toFloat : (self : Int) -> Float = Prim.intToFloat; - - /// Converts a signed integer with infinite precision to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toInt8(123) == (123 : Int8); - /// ``` - public let toInt8 : (self : Int) -> Int8 = Prim.intToInt8; - - /// Converts a signed integer with infinite precision to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toInt16(12_345) == (12_345 : Int16); - /// ``` - public let toInt16 : (self : Int) -> Int16 = Prim.intToInt16; - - /// Converts a signed integer with infinite precision to a 32-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toInt32(123_456) == (123_456 : Int32); - /// ``` - public let toInt32 : (self : Int) -> Int32 = Prim.intToInt32; - - /// Converts a signed integer with infinite precision to a 64-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int.toInt64(123_456_789) == (123_456_789 : Int64); - /// ``` - public let toInt64 : (self : Int) -> Int64 = Prim.intToInt64; - - /// Converts an 8-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromInt8(123 : Int8) == 123; - /// ``` - public let fromInt8 : (x : Int8) -> Int = Prim.int8ToInt; - - /// Converts a 16-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromInt16(12_345 : Int16) == 12_345; - /// ``` - public let fromInt16 : (x : Int16) -> Int = Prim.int16ToInt; - - /// Converts a 32-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromInt32(123_456 : Int32) == 123_456; - /// ``` - public let fromInt32 : (x : Int32) -> Int = Prim.int32ToInt; - - /// Converts a 64-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.fromInt64(123_456_789 : Int64) == 123_456_789; - /// ``` - public let fromInt64 : (x : Int64) -> Int = Prim.int64ToInt; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.min(2, -3) == -3; - /// ``` - public func min(x : Int, y : Int) : Int { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.max(2, -3) == 2; - /// ``` - public func max(x : Int, y : Int) : Int { - if (x < y) { y } else { x } - }; - - /// Equality function for Int types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int = 1; - /// let b : Int = -1; - /// assert not Int.equal(a, b); - /// ``` - public func equal(x : Int, y : Int) : Bool { x == y }; - - /// Inequality function for Int types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int, y : Int) : Bool { x != y }; - - /// "Less than" function for Int types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int, y : Int) : Bool { x < y }; - - /// "Less than or equal" function for Int types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.lessOrEqual(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int, y : Int) : Bool { x <= y }; - - /// "Greater than" function for Int types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.greater(1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int, y : Int) : Bool { x > y }; - - /// "Greater than or equal" function for Int types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.greaterOrEqual(1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int, y : Int) : Bool { x >= y }; - - /// General-purpose comparison function for `Int`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3], Int.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int, y : Int) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x` . - /// - /// Example: - /// ```motoko include=import - /// assert Int.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int) : Int { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.add(1, -2) == -1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int.add) == -4; - /// ``` - public func add(x : Int, y : Int) : Int { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.sub(1, 2) == -1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int.sub) == 4; - /// ``` - public func sub(x : Int, y : Int) : Int { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.mul(-2, 3) == -6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int.mul) == 6; - /// ``` - public func mul(x : Int, y : Int) : Int { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int.div(6, -2) == -3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int, y : Int) : Int { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int.rem(6, -4) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int, y : Int) : Int { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps when `y` is negative or `y > 2 ** 32 - 1`. - /// No overflow since `Int` has infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int.pow(-2, 3) == -8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int, y : Int) : Int { x ** y }; - - /// Returns an iterator over the integers from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int, toExclusive : Int) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int { - if (n >= toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int` values from the first to second argument with an exclusive upper bound, - /// incrementing by the specified step size. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// // Positive step - /// let iter1 = Int.rangeBy(1, 7, 2); - /// assert iter1.next() == ?1; - /// assert iter1.next() == ?3; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// // Negative step - /// let iter2 = Int.rangeBy(7, 1, -2); - /// assert iter2.next() == ?7; - /// assert iter2.next() == ?5; - /// assert iter2.next() == ?3; - /// assert iter2.next() == null; - /// ``` - /// - /// If `step` is 0 or if the iteration would not progress towards the bound, returns an empty iterator. - public func rangeBy(fromInclusive : Int, toExclusive : Int, step : Int) : Iter.Iter { - if (step == 0) { - Iter.empty() - } else if (step > 0 and fromInclusive < toExclusive) { - object { - var n = fromInclusive; - public func next() : ?Int { - if (n >= toExclusive) { - null - } else { - let current = n; - n += step; - ?current - } - } - } - } else if (step < 0 and fromInclusive > toExclusive) { - object { - var n = fromInclusive; - public func next() : ?Int { - if (n <= toExclusive) { - null - } else { - let current = n; - n += step; - ?current - } - } - } - } else { - Iter.empty() - } - }; - - /// Returns an iterator over the integers from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int.rangeInclusive(3, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int, to : Int) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - public func next() : ?Int { - if (n > to) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over the integers from the first to second argument, inclusive, - /// incrementing by the specified step size. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// // Positive step - /// let iter1 = Int.rangeByInclusive(1, 7, 2); - /// assert iter1.next() == ?1; - /// assert iter1.next() == ?3; - /// assert iter1.next() == ?5; - /// assert iter1.next() == ?7; - /// assert iter1.next() == null; - /// - /// // Negative step - /// let iter2 = Int.rangeByInclusive(7, 1, -2); - /// assert iter2.next() == ?7; - /// assert iter2.next() == ?5; - /// assert iter2.next() == ?3; - /// assert iter2.next() == ?1; - /// assert iter2.next() == null; - /// ``` - /// - /// If `from == to`, return an iterator which only returns that value. - /// - /// Otherwise, if `step` is 0 or if the iteration would not progress towards the bound, returns an empty iterator. - public func rangeByInclusive(from : Int, to : Int, step : Int) : Iter.Iter { - if (from == to) { - Iter.singleton(from) - } else if (step == 0) { - Iter.empty() - } else if (step > 0 and from < to) { - object { - var n = from; - public func next() : ?Int { - if (n >= to + 1) { - null - } else { - let current = n; - n += step; - ?current - } - } - } - } else if (step < 0 and from > to) { - object { - var n = from; - public func next() : ?Int { - if (n + 1 <= to) { - null - } else { - let current = n; - n += step; - ?current - } - } - } - } else { - Iter.empty() - } - }; - -} diff --git a/.mops/core@2.5.0/src/Int16.mo b/.mops/core@2.5.0/src/Int16.mo deleted file mode 100644 index 40b3b6d..0000000 --- a/.mops/core@2.5.0/src/Int16.mo +++ /dev/null @@ -1,774 +0,0 @@ -/// Utility functions on 16-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int16 "mo:core/Int16"; -/// ``` - -import Int "Int"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 16-bit signed integers. - public type Int16 = Prim.Types.Int16; - - /// Minimum 16-bit integer value, `-2 ** 15`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.minValue == (-32_768 : Int16); - /// ``` - public let minValue : Int16 = -32_768; - - /// Maximum 16-bit integer value, `+2 ** 15 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.maxValue == (+32_767 : Int16); - /// ``` - public let maxValue : Int16 = 32_767; - - /// Converts a 16-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toInt(12_345) == (12_345 : Int); - /// ``` - public let toInt : (self : Int16) -> Int = Prim.int16ToInt; - - /// Converts a signed integer with infinite precision to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromInt(12_345) == (+12_345 : Int16); - /// ``` - public let fromInt : Int -> Int16 = Prim.intToInt16; - - /// Converts a signed integer with infinite precision to a 16-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromIntWrap(-12_345) == (-12_345 : Int); - /// ``` - public let fromIntWrap : Int -> Int16 = Prim.intToInt16Wrap; - - /// Converts a 8-bit signed integer to a 16-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromInt8(-123) == (-123 : Int16); - /// ``` - public let fromInt8 : Int8 -> Int16 = Prim.int8ToInt16; - - /// Converts a 16-bit signed integer to a 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toInt8(-123) == (-123 : Int8); - /// ``` - public let toInt8 : (self : Int16) -> Int8 = Prim.int16ToInt8; - - /// Converts a 32-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromInt32(-12_345) == (-12_345 : Int16); - /// ``` - public let fromInt32 : Int32 -> Int16 = Prim.int32ToInt16; - - /// Converts a 16-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toInt32(-12_345) == (-12_345 : Int32); - /// ``` - public let toInt32 : (self : Int16) -> Int32 = Prim.int16ToInt32; - - /// Converts a 64-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromInt64(-12_345) == (-12_345 : Int16); - /// ``` - public func fromInt64(x : Int64) : Int16 { - Prim.int32ToInt16(Prim.int64ToInt32(x)) - }; - - /// Converts a 16-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toInt64(-12_345) == (-12_345 : Int64); - /// ``` - public func toInt64(self : Int16) : Int64 { - Prim.int32ToInt64(Prim.int16ToInt32(self)) - }; - - /// Converts an unsigned 16-bit integer to a signed 16-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.fromNat16(12_345) == (+12_345 : Int16); - /// ``` - public let fromNat16 : Nat16 -> Int16 = Prim.nat16ToInt16; - - /// Converts a signed 16-bit integer to an unsigned 16-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toNat16(-1) == (65_535 : Nat16); // underflow - /// ``` - public let toNat16 : (self : Int16) -> Nat16 = Prim.int16ToNat16; - - /// Returns the Text representation of `x`. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.toText(-12345) == "-12345"; - /// ``` - public func toText(self : Int16) : Text { - Int.toText(toInt(self)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 15` (the minimum `Int16` value). - /// - /// Example: - /// ```motoko include=import - /// assert Int16.abs(-12345) == +12_345; - /// ``` - public func abs(x : Int16) : Int16 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.min(+2, -3) == -3; - /// ``` - public func min(x : Int16, y : Int16) : Int16 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.max(+2, -3) == +2; - /// ``` - public func max(x : Int16, y : Int16) : Int16 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int16 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int16 = -123; - /// let b : Int16 = 123; - /// assert not Int16.equal(a, b); - /// ``` - public func equal(x : Int16, y : Int16) : Bool { x == y }; - - /// Inequality function for Int16 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int16, y : Int16) : Bool { x != y }; - - /// "Less than" function for Int16 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int16, y : Int16) : Bool { x < y }; - - /// "Less than or equal" function for Int16 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.lessOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int16, y : Int16) : Bool { x <= y }; - - /// "Greater than" function for Int16 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert not Int16.greater(-2, 1); - /// ``` - public func greater(x : Int16, y : Int16) : Bool { x > y }; - - /// "Greater than or equal" function for Int16 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.greaterOrEqual(-2, -2); - /// ``` - public func greaterOrEqual(x : Int16, y : Int16) : Bool { - x >= y - }; - - /// General-purpose comparison function for `Int16`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3] : [Int16], Int16.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int16, y : Int16) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 15)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int16) : Int16 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.add(100, 23) == +123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int16.add) == -4; - /// ``` - public func add(x : Int16, y : Int16) : Int16 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.sub(123, 100) == +23; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int16.sub) == 4; - /// ``` - public func sub(x : Int16, y : Int16) : Int16 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.mul(12, 10) == +120; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int16.mul) == 6; - /// ``` - public func mul(x : Int16, y : Int16) : Int16 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.div(123, 10) == +12; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int16, y : Int16) : Int16 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.rem(123, 10) == +3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int16, y : Int16) : Int16 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 16`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.pow(2, 10) == +1_024; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int16, y : Int16) : Int16 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitnot(-256 /* 0xff00 */) == +255 // 0xff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int16) : Int16 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitand(0x0fff, 0x00f0) == +240 // 0xf0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int16, y : Int16) : Int16 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitor(0x0f0f, 0x00f0) == +4_095 // 0x0fff; - /// ``` - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int16, y : Int16) : Int16 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitxor(0x0fff, 0x00f0) == +3_855 // 0x0f0f; - /// ``` - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int16, y : Int16) : Int16 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 16`, the semantics is the same as for `bitshiftLeft(x, y % 16)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitshiftLeft(1, 8) == +256 // 0x100 equivalent to `2 ** 8`.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int16, y : Int16) : Int16 { - x << y - }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 16`, the semantics is the same as for `bitshiftRight(x, y % 16)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitshiftRight(1024, 8) == +4 // equivalent to `1024 / (2 ** 8)`; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int16, y : Int16) : Int16 { - x >> y - }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 16`, the semantics is the same as for `bitrotLeft(x, y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitrotLeft(0x2001, 4) == +18 // 0x12.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int16, y : Int16) : Int16 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 16`, the semantics is the same as for `bitrotRight(x, y % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitrotRight(0x2010, 8) == +4_128 // 0x01020.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int16, y : Int16) : Int16 { - x <>> y - }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 16`, the semantics is the same as for `bittest(x, p % 16)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bittest(128, 7); - /// ``` - public func bittest(x : Int16, p : Nat) : Bool { - Prim.btstInt16(x, Prim.intToInt16(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 16`, the semantics is the same as for `bitset(x, p % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitset(0, 7) == +128; - /// ``` - public func bitset(x : Int16, p : Nat) : Int16 { - x | (1 << Prim.intToInt16(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 16`, the semantics is the same as for `bitclear(x, p % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitclear(-1, 7) == -129; - /// ``` - public func bitclear(x : Int16, p : Nat) : Int16 { - x & ^(1 << Prim.intToInt16(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 16`, the semantics is the same as for `bitclear(x, p % 16)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitflip(255, 7) == +127; - /// ``` - public func bitflip(x : Int16, p : Nat) : Int16 { - x ^ (1 << Prim.intToInt16(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitcountNonZero(0xff) == +8; - /// ``` - public let bitcountNonZero : (x : Int16) -> Int16 = Prim.popcntInt16; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitcountLeadingZero(0x80) == +8; - /// ``` - public let bitcountLeadingZero : (x : Int16) -> Int16 = Prim.clzInt16; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.bitcountTrailingZero(0x0100) == +8; - /// ``` - public let bitcountTrailingZero : (x : Int16) -> Int16 = Prim.ctzInt16; - - /// Returns the upper (i.e. most significant) and lower (least significant) byte of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.explode 0x77ee == (119, 238); - /// ``` - public let explode : (x : Int16) -> (msb : Nat8, lsb : Nat8) = Prim.explodeInt16; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.addWrap(2 ** 14, 2 ** 14) == -32_768; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int16, y : Int16) : Int16 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.subWrap(-2 ** 15, 1) == +32_767; // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int16, y : Int16) : Int16 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int16.mulWrap(2 ** 8, 2 ** 8) == 0; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int16, y : Int16) : Int16 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 16`. - /// - /// Example: - /// ```motoko include=import - /// - /// assert Int16.powWrap(2, 15) == -32_768; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int16, y : Int16) : Int16 { x **% y }; - - /// Returns an iterator over `Int16` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int16, toExclusive : Int16) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int16 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int16` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int16, to : Int16) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Int16 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Int16 values, from minValue to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int16.allValues(); - /// assert iter.next() == ?-32_768; - /// assert iter.next() == ?-32_767; - /// assert iter.next() == ?-32_766; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(minValue, maxValue) - }; - -} diff --git a/.mops/core@2.5.0/src/Int32.mo b/.mops/core@2.5.0/src/Int32.mo deleted file mode 100644 index 947b76b..0000000 --- a/.mops/core@2.5.0/src/Int32.mo +++ /dev/null @@ -1,787 +0,0 @@ -/// Utility functions on 32-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int32 "mo:core/Int32"; -/// ``` -import Int "Int"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 32-bit signed integers. - public type Int32 = Prim.Types.Int32; - - /// Minimum 32-bit integer value, `-2 ** 31`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.minValue == -2_147_483_648; - /// ``` - public let minValue : Int32 = -2_147_483_648; - - /// Maximum 32-bit integer value, `+2 ** 31 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.maxValue == +2_147_483_647; - /// ``` - public let maxValue : Int32 = 2_147_483_647; - - /// Converts a 32-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toInt(123_456) == (123_456 : Int); - /// ``` - public let toInt : (self : Int32) -> Int = Prim.int32ToInt; - - /// Converts a signed integer with infinite precision to a 32-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromInt(123_456) == (+123_456 : Int32); - /// ``` - public let fromInt : Int -> Int32 = Prim.intToInt32; - - /// Converts a signed integer with infinite precision to a 32-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromIntWrap(-123_456) == (-123_456 : Int); - /// ``` - public let fromIntWrap : Int -> Int32 = Prim.intToInt32Wrap; - - /// Converts a 16-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromInt16(-123) == (-123 : Int32); - /// ``` - public let fromInt16 : Int16 -> Int32 = Prim.int16ToInt32; - - /// Converts an 8-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromInt8(-123) == (-123 : Int32); - /// ``` - public func fromInt8(x : Int8) : Int32 { - Prim.int16ToInt32(Prim.int8ToInt16(x)) - }; - - /// Converts a 32-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toInt8(-123) == (-123 : Int8); - /// ``` - public func toInt8(self : Int32) : Int8 { - Prim.int16ToInt8(Prim.int32ToInt16(self)) - }; - - /// Converts a 32-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toInt16(-123) == (-123 : Int16); - /// ``` - public func toInt16(self : Int32) : Int16 { - Prim.int32ToInt16(self) - }; - - /// Converts a 64-bit signed integer to a 32-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromInt64(-123_456) == (-123_456 : Int32); - /// ``` - public let fromInt64 : Int64 -> Int32 = Prim.int64ToInt32; - - /// Converts a 32-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toInt64(-123_456) == (-123_456 : Int64); - /// ``` - public let toInt64 : (self : Int32) -> Int64 = Prim.int32ToInt64; - - /// Converts an unsigned 32-bit integer to a signed 32-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.fromNat32(123_456) == (+123_456 : Int32); - /// ``` - public let fromNat32 : Nat32 -> Int32 = Prim.nat32ToInt32; - - /// Converts a signed 32-bit integer to an unsigned 32-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toNat32(-1) == (4_294_967_295 : Nat32); // underflow - /// ``` - public let toNat32 : (self : Int32) -> Nat32 = Prim.int32ToNat32; - - /// Returns the Text representation of `x`. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.toText(-123456) == "-123456"; - /// ``` - public func toText(self : Int32) : Text { - Int.toText(toInt(self)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 31` (the minimum `Int32` value). - /// - /// Example: - /// ```motoko include=import - /// assert Int32.abs(-123456) == +123_456; - /// ``` - public func abs(x : Int32) : Int32 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.min(+2, -3) == -3; - /// ``` - public func min(x : Int32, y : Int32) : Int32 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.max(+2, -3) == +2; - /// ``` - public func max(x : Int32, y : Int32) : Int32 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int32 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int32 = -123; - /// let b : Int32 = 123; - /// assert not Int32.equal(a, b); - /// ``` - public func equal(x : Int32, y : Int32) : Bool { x == y }; - - /// Inequality function for Int32 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int32, y : Int32) : Bool { x != y }; - - /// "Less than" function for Int32 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int32, y : Int32) : Bool { x < y }; - - /// "Less than or equal" function for Int32 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.lessOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int32, y : Int32) : Bool { x <= y }; - - /// "Greater than" function for Int32 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.greater(-2, -3); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int32, y : Int32) : Bool { x > y }; - - /// "Greater than or equal" function for Int32 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.greaterOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int32, y : Int32) : Bool { - x >= y - }; - - /// General-purpose comparison function for `Int32`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3] : [Int32], Int32.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int32, y : Int32) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 31)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int32) : Int32 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.add(100, 23) == +123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int32.add) == -4; - /// ``` - public func add(x : Int32, y : Int32) : Int32 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.sub(1234, 123) == +1_111; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int32.sub) == 4; - /// ``` - public func sub(x : Int32, y : Int32) : Int32 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.mul(123, 100) == +12_300; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int32.mul) == 6; - /// ``` - public func mul(x : Int32, y : Int32) : Int32 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.div(123, 10) == +12; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int32, y : Int32) : Int32 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.rem(123, 10) == +3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int32, y : Int32) : Int32 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 32`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.pow(2, 10) == +1_024; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int32, y : Int32) : Int32 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitnot(-256 /* 0xffff_ff00 */) == +255 // 0xff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int32) : Int32 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitand(0xffff, 0x00f0) == +240 // 0xf0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int32, y : Int32) : Int32 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitor(0xffff, 0x00f0) == +65_535 // 0xffff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int32, y : Int32) : Int32 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitxor(0xffff, 0x00f0) == +65_295 // 0xff0f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int32, y : Int32) : Int32 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 32`, the semantics is the same as for `bitshiftLeft(x, y % 32)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitshiftLeft(1, 8) == +256 // 0x100 equivalent to `2 ** 8`.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int32, y : Int32) : Int32 { - x << y - }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 32`, the semantics is the same as for `bitshiftRight(x, y % 32)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitshiftRight(1024, 8) == +4 // equivalent to `1024 / (2 ** 8)`; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int32, y : Int32) : Int32 { - x >> y - }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 32`, the semantics is the same as for `bitrotLeft(x, y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitrotLeft(0x2000_0001, 4) == +18 // 0x12.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int32, y : Int32) : Int32 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 32`, the semantics is the same as for `bitrotRight(x, y % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitrotRight(0x0002_0001, 8) == +16_777_728 // 0x0100_0200.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int32, y : Int32) : Int32 { - x <>> y - }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 32`, the semantics is the same as for `bittest(x, p % 32)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bittest(128, 7); - /// ``` - public func bittest(x : Int32, p : Nat) : Bool { - Prim.btstInt32(x, Prim.intToInt32(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 32`, the semantics is the same as for `bitset(x, p % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitset(0, 7) == +128; - /// ``` - public func bitset(x : Int32, p : Nat) : Int32 { - x | (1 << Prim.intToInt32(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 32`, the semantics is the same as for `bitclear(x, p % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitclear(-1, 7) == -129; - /// ``` - public func bitclear(x : Int32, p : Nat) : Int32 { - x & ^(1 << Prim.intToInt32(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 32`, the semantics is the same as for `bitclear(x, p % 32)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitflip(255, 7) == +127; - /// ``` - public func bitflip(x : Int32, p : Nat) : Int32 { - x ^ (1 << Prim.intToInt32(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitcountNonZero(0xffff) == +16; - /// ``` - public let bitcountNonZero : (x : Int32) -> Int32 = Prim.popcntInt32; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitcountLeadingZero(0x8000) == +16; - /// ``` - public let bitcountLeadingZero : (x : Int32) -> Int32 = Prim.clzInt32; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.bitcountTrailingZero(0x0201_0000) == +16; - /// ``` - public let bitcountTrailingZero : (x : Int32) -> Int32 = Prim.ctzInt32; - - /// Returns the upper (i.e. most significant), lower (least significant) - /// and in-between bytes of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.explode 0x66885511 == (102, 136, 85, 17); - /// ``` - public let explode : (x : Int32) -> (msb : Nat8, Nat8, Nat8, lsb : Nat8) = Prim.explodeInt32; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.addWrap(2 ** 30, 2 ** 30) == -2_147_483_648; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int32, y : Int32) : Int32 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.subWrap(-2 ** 31, 1) == +2_147_483_647; // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int32, y : Int32) : Int32 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.mulWrap(2 ** 16, 2 ** 16) == 0; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int32, y : Int32) : Int32 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 32`. - /// - /// Example: - /// ```motoko include=import - /// assert Int32.powWrap(2, 31) == -2_147_483_648; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int32, y : Int32) : Int32 { x **% y }; - - /// Returns an iterator over `Int32` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int32, toExclusive : Int32) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int32 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int32` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int32, to : Int32) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Int32 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Int32 values, from minValue to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int32.allValues(); - /// assert iter.next() == ?-2_147_483_648; - /// assert iter.next() == ?-2_147_483_647; - /// assert iter.next() == ?-2_147_483_646; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(minValue, maxValue) - }; - -} diff --git a/.mops/core@2.5.0/src/Int64.mo b/.mops/core@2.5.0/src/Int64.mo deleted file mode 100644 index 95f5647..0000000 --- a/.mops/core@2.5.0/src/Int64.mo +++ /dev/null @@ -1,796 +0,0 @@ -/// Utility functions on 64-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int64 "mo:core/Int64"; -/// ``` - -import Int "Int"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 64-bit signed integers. - public type Int64 = Prim.Types.Int64; - - /// Minimum 64-bit integer value, `-2 ** 63`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.minValue == -9_223_372_036_854_775_808; - /// ``` - public let minValue : Int64 = -9_223_372_036_854_775_808; - - /// Maximum 64-bit integer value, `+2 ** 63 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.maxValue == +9_223_372_036_854_775_807; - /// ``` - public let maxValue : Int64 = 9_223_372_036_854_775_807; - - /// Converts a 64-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toInt(123_456) == (123_456 : Int); - /// ``` - public let toInt : (self : Int64) -> Int = Prim.int64ToInt; - - /// Converts a signed integer with infinite precision to a 64-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromInt(123_456) == (+123_456 : Int64); - /// ``` - public let fromInt : (x : Int) -> Int64 = Prim.intToInt64; - - /// Converts a 32-bit signed integer to a 64-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromInt32(-123_456) == (-123_456 : Int64); - /// ``` - public let fromInt32 : (x : Int32) -> Int64 = Prim.int32ToInt64; - - /// Converts a 16-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromInt16(-123) == (-123 : Int64); - /// ``` - public func fromInt16(x : Int16) : Int64 { - Prim.int32ToInt64(Prim.int16ToInt32(x)) - }; - - /// Converts an 8-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromInt8(-123) == (-123 : Int64); - /// ``` - public func fromInt8(x : Int8) : Int64 { - Prim.int32ToInt64(Prim.int16ToInt32(Prim.int8ToInt16(x))) - }; - - /// Converts a 64-bit signed integer to a 32-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toInt32(-123_456) == (-123_456 : Int32); - /// ``` - public func toInt32(self : Int64) : Int32 { - Prim.int64ToInt32(self) - }; - - /// Converts a 64-bit signed integer to a 16-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toInt16(-123) == (-123 : Int16); - /// ``` - public func toInt16(self : Int64) : Int16 { - Prim.int32ToInt16(Prim.int64ToInt32(self)) - }; - - /// Converts a 64-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toInt8(-123) == (-123 : Int8); - /// ``` - public func toInt8(self : Int64) : Int8 { - Prim.int16ToInt8(Prim.int32ToInt16(Prim.int64ToInt32(self))) - }; - - /// Converts a signed integer with infinite precision to a 64-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromIntWrap(-123_456) == (-123_456 : Int64); - /// ``` - public let fromIntWrap : Int -> Int64 = Prim.intToInt64Wrap; - - /// Converts an unsigned 64-bit integer to a signed 64-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.fromNat64(123_456) == (+123_456 : Int64); - /// ``` - public let fromNat64 : Nat64 -> Int64 = Prim.nat64ToInt64; - - /// Converts a signed 64-bit integer to an unsigned 64-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toNat64(-1) == (18_446_744_073_709_551_615 : Nat64); // underflow - /// ``` - public let toNat64 : (self : Int64) -> Nat64 = Prim.int64ToNat64; - - /// Returns the Text representation of `x`. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// - /// Example: - /// ```motoko include=import - /// assert Int64.toText(-123456) == "-123456"; - /// ``` - public func toText(self : Int64) : Text { - Int.toText(toInt(self)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 63` (the minimum `Int64` value). - /// - /// Example: - /// ```motoko include=import - /// assert Int64.abs(-123456) == +123_456; - /// ``` - public func abs(x : Int64) : Int64 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.min(+2, -3) == -3; - /// ``` - public func min(x : Int64, y : Int64) : Int64 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.max(+2, -3) == +2; - /// ``` - public func max(x : Int64, y : Int64) : Int64 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int64 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int64 = -123; - /// let b : Int64 = 123; - /// assert not Int64.equal(a, b); - /// ``` - public func equal(x : Int64, y : Int64) : Bool { x == y }; - - /// Inequality function for Int64 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int64, y : Int64) : Bool { x != y }; - - /// "Less than" function for Int64 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int64, y : Int64) : Bool { x < y }; - - /// "Less than or equal" function for Int64 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.lessOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int64, y : Int64) : Bool { x <= y }; - - /// "Greater than" function for Int64 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.greater(-2, -3); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int64, y : Int64) : Bool { x > y }; - - /// "Greater than or equal" function for Int64 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.greaterOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int64, y : Int64) : Bool { - x >= y - }; - - /// General-purpose comparison function for `Int64`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3] : [Int64], Int64.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int64, y : Int64) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 63)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int64) : Int64 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.add(1234, 123) == +1_357; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int64.add) == -4; - /// ``` - public func add(x : Int64, y : Int64) : Int64 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.sub(123, 100) == +23; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int64.sub) == 4; - /// ``` - public func sub(x : Int64, y : Int64) : Int64 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.mul(123, 10) == +1_230; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int64.mul) == 6; - /// ``` - public func mul(x : Int64, y : Int64) : Int64 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.div(123, 10) == +12; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int64, y : Int64) : Int64 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.rem(123, 10) == +3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int64, y : Int64) : Int64 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 64`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.pow(2, 10) == +1_024; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int64, y : Int64) : Int64 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitnot(-256 /* 0xffff_ffff_ffff_ff00 */) == +255 // 0xff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int64) : Int64 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitand(0xffff, 0x00f0) == +240 // 0xf0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int64, y : Int64) : Int64 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitor(0xffff, 0x00f0) == +65_535 // 0xffff; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int64, y : Int64) : Int64 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitxor(0xffff, 0x00f0) == +65_295 // 0xff0f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int64, y : Int64) : Int64 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 64`, the semantics is the same as for `bitshiftLeft(x, y % 64)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitshiftLeft(1, 8) == +256 // 0x100 equivalent to `2 ** 8`.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int64, y : Int64) : Int64 { - x << y - }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 64`, the semantics is the same as for `bitshiftRight(x, y % 64)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitshiftRight(1024, 8) == +4 // equivalent to `1024 / (2 ** 8)`; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int64, y : Int64) : Int64 { - x >> y - }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 64`, the semantics is the same as for `bitrotLeft(x, y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// - /// assert Int64.bitrotLeft(0x2000_0000_0000_0001, 4) == +18 // 0x12.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int64, y : Int64) : Int64 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 64`, the semantics is the same as for `bitrotRight(x, y % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitrotRight(0x0002_0000_0000_0001, 48) == +65538 // 0x1_0002.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int64, y : Int64) : Int64 { - x <>> y - }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 64`, the semantics is the same as for `bittest(x, p % 64)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bittest(128, 7); - /// ``` - public func bittest(x : Int64, p : Nat) : Bool { - Prim.btstInt64(x, Prim.intToInt64(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 64`, the semantics is the same as for `bitset(x, p % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitset(0, 7) == +128; - /// ``` - public func bitset(x : Int64, p : Nat) : Int64 { - x | (1 << Prim.intToInt64(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 64`, the semantics is the same as for `bitclear(x, p % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitclear(-1, 7) == -129; - /// ``` - public func bitclear(x : Int64, p : Nat) : Int64 { - x & ^(1 << Prim.intToInt64(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 64`, the semantics is the same as for `bitclear(x, p % 64)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitflip(255, 7) == +127; - /// ``` - public func bitflip(x : Int64, p : Nat) : Int64 { - x ^ (1 << Prim.intToInt64(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitcountNonZero(0xffff) == +16; - /// ``` - public let bitcountNonZero : (x : Int64) -> Int64 = Prim.popcntInt64; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitcountLeadingZero(0x8000_0000) == +32; - /// ``` - public let bitcountLeadingZero : (x : Int64) -> Int64 = Prim.clzInt64; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.bitcountTrailingZero(0x0201_0000) == +16; - /// ``` - public let bitcountTrailingZero : (x : Int64) -> Int64 = Prim.ctzInt64; - - /// Returns the upper (i.e. most significant), lower (least significant) - /// and in-between bytes of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.explode 0x33772266aa885511 == (51, 119, 34, 102, 170, 136, 85, 17); - /// ``` - public let explode : (x : Int64) -> (msb : Nat8, Nat8, Nat8, Nat8, Nat8, Nat8, Nat8, lsb : Nat8) = Prim.explodeInt64; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.addWrap(2 ** 62, 2 ** 62) == -9_223_372_036_854_775_808; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int64, y : Int64) : Int64 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.subWrap(-2 ** 63, 1) == +9_223_372_036_854_775_807; // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int64, y : Int64) : Int64 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.mulWrap(2 ** 32, 2 ** 32) == 0; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int64, y : Int64) : Int64 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 64`. - /// - /// Example: - /// ```motoko include=import - /// assert Int64.powWrap(2, 63) == -9_223_372_036_854_775_808; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int64, y : Int64) : Int64 { x **% y }; - - /// Returns an iterator over `Int64` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int64, toExclusive : Int64) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int64 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int64` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int64, to : Int64) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Int64 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Int64 values, from minValue to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int64.allValues(); - /// assert iter.next() == ?-9_223_372_036_854_775_808; - /// assert iter.next() == ?-9_223_372_036_854_775_807; - /// assert iter.next() == ?-9_223_372_036_854_775_806; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(minValue, maxValue) - }; - -} diff --git a/.mops/core@2.5.0/src/Int8.mo b/.mops/core@2.5.0/src/Int8.mo deleted file mode 100644 index ffde265..0000000 --- a/.mops/core@2.5.0/src/Int8.mo +++ /dev/null @@ -1,771 +0,0 @@ -/// Utility functions on 8-bit signed integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Int8 "mo:core/Int8"; -/// ``` -import Int "Int"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 8-bit signed integers. - public type Int8 = Prim.Types.Int8; - - /// Minimum 8-bit integer value, `-2 ** 7`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.minValue == -128; - /// ``` - public let minValue : Int8 = -128; - - /// Maximum 8-bit integer value, `+2 ** 7 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.maxValue == +127; - /// ``` - public let maxValue : Int8 = 127; - - /// Converts an 8-bit signed integer to a signed integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toInt(123) == (123 : Int); - /// ``` - public let toInt : (self : Int8) -> Int = Prim.int8ToInt; - - /// Converts a signed integer with infinite precision to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromInt(123) == (+123 : Int8); - /// ``` - public let fromInt : Int -> Int8 = Prim.intToInt8; - - /// Converts a signed integer with infinite precision to an 8-bit signed integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromIntWrap(-123) == (-123 : Int8); - /// ``` - public let fromIntWrap : Int -> Int8 = Prim.intToInt8Wrap; - - /// Converts a 16-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromInt16(123) == (+123 : Int8); - /// ``` - public let fromInt16 : Int16 -> Int8 = Prim.int16ToInt8; - - /// Converts an 8-bit signed integer to a 16-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toInt16(123) == (+123 : Int16); - /// ``` - public let toInt16 : (self : Int8) -> Int16 = Prim.int8ToInt16; - - /// Converts a 32-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromInt32(123) == (+123 : Int8); - /// ``` - public func fromInt32(x : Int32) : Int8 { - Prim.int16ToInt8(Prim.int32ToInt16(x)) - }; - - /// Converts an 8-bit signed integer to a 32-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toInt32(123) == (+123 : Int32); - /// ``` - public func toInt32(self : Int8) : Int32 { - Prim.int16ToInt32(Prim.int8ToInt16(self)) - }; - - /// Converts a 64-bit signed integer to an 8-bit signed integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromInt64(123) == (+123 : Int8); - /// ``` - public func fromInt64(x : Int64) : Int8 { - Prim.int16ToInt8(Prim.int32ToInt16(Prim.int64ToInt32(x))) - }; - - /// Converts an 8-bit signed integer to a 64-bit signed integer. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toInt64(123) == (+123 : Int64); - /// ``` - public func toInt64(self : Int8) : Int64 { - Prim.int32ToInt64(Prim.int16ToInt32(Prim.int8ToInt16(self))) - }; - - /// Converts an unsigned 8-bit integer to a signed 8-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.fromNat8(123) == (+123 : Int8); - /// ``` - public let fromNat8 : Nat8 -> Int8 = Prim.nat8ToInt8; - - /// Converts a signed 8-bit integer to an unsigned 8-bit integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toNat8(-1) == (255 : Nat8); // underflow - /// ``` - public let toNat8 : (self : Int8) -> Nat8 = Prim.int8ToNat8; - - /// Converts an integer number to its textual representation. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.toText(-123) == "-123"; - /// ``` - public func toText(self : Int8) : Text { - Int.toText(toInt(self)) - }; - - /// Returns the absolute value of `x`. - /// - /// Traps when `x == -2 ** 7` (the minimum `Int8` value). - /// - /// Example: - /// ```motoko include=import - /// assert Int8.abs(-123) == +123; - /// ``` - public func abs(x : Int8) : Int8 { - fromInt(Int.abs(toInt(x))) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.min(+2, -3) == -3; - /// ``` - public func min(x : Int8, y : Int8) : Int8 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.max(+2, -3) == +2; - /// ``` - public func max(x : Int8, y : Int8) : Int8 { - if (x < y) { y } else { x } - }; - - /// Equality function for Int8 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.equal(-1, -1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Int8 = -123; - /// let b : Int8 = 123; - /// assert not Int8.equal(a, b); - /// ``` - public func equal(x : Int8, y : Int8) : Bool { x == y }; - - /// Inequality function for Int8 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.notEqual(-1, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Int8, y : Int8) : Bool { x != y }; - - /// "Less than" function for Int8 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.less(-2, 1); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Int8, y : Int8) : Bool { x < y }; - - /// "Less than or equal" function for Int8 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.lessOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Int8, y : Int8) : Bool { x <= y }; - - /// "Greater than" function for Int8 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.greater(-2, -3); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Int8, y : Int8) : Bool { x > y }; - - /// "Greater than or equal" function for Int8 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.greaterOrEqual(-2, -2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Int8, y : Int8) : Bool { x >= y }; - - /// General-purpose comparison function for `Int8`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.compare(-3, 2) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([1, -2, -3] : [Int8], Int8.compare) == [-3, -2, 1]; - /// ``` - public func compare(x : Int8, y : Int8) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the negation of `x`, `-x`. - /// - /// Traps on overflow, i.e. for `neg(-2 ** 7)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.neg(123) == -123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - public func neg(x : Int8) : Int8 { -x }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.add(100, 23) == +123; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int8.add) == -4; - /// ``` - public func add(x : Int8, y : Int8) : Int8 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.sub(123, 23) == +100; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 0, Int8.sub) == 4; - /// ``` - public func sub(x : Int8, y : Int8) : Int8 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.mul(12, 10) == +120; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([1, -2, -3], 1, Int8.mul) == 6; - /// ``` - public func mul(x : Int8, y : Int8) : Int8 { x * y }; - - /// Returns the signed integer division of `x` by `y`, `x / y`. - /// Rounds the quotient towards zero, which is the same as truncating the decimal places of the quotient. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.div(123, 10) == +12; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Int8, y : Int8) : Int8 { x / y }; - - /// Returns the remainder of the signed integer division of `x` by `y`, `x % y`, - /// which is defined as `x - x / y * y`. - /// - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.rem(123, 10) == +3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Int8, y : Int8) : Int8 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// - /// Traps on overflow/underflow and when `y < 0 or y >= 8`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.pow(2, 6) == +64; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Int8, y : Int8) : Int8 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitnot(-16 /* 0xf0 */) == +15 // 0x0f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Int8) : Int8 { ^x }; - - /// Returns the bitwise "and" of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitand(0x1f, 0x70) == +16 // 0x10; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Int8, y : Int8) : Int8 { x & y }; - - /// Returns the bitwise "or" of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitor(0x0f, 0x70) == +127 // 0x7f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Int8, y : Int8) : Int8 { x | y }; - - /// Returns the bitwise "exclusive or" of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitxor(0x70, 0x7f) == +15 // 0x0f; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Int8, y : Int8) : Int8 { x ^ y }; - - /// Returns the bitwise left shift of `x` by `y`, `x << y`. - /// The right bits of the shift filled with zeros. - /// Left-overflowing bits, including the sign bit, are discarded. - /// - /// For `y >= 8`, the semantics is the same as for `bitshiftLeft(x, y % 8)`. - /// For `y < 0`, the semantics is the same as for `bitshiftLeft(x, y + y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitshiftLeft(1, 4) == +16 // 0x10 equivalent to `2 ** 4`.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Int8, y : Int8) : Int8 { x << y }; - - /// Returns the signed bitwise right shift of `x` by `y`, `x >> y`. - /// The sign bit is retained and the left side is filled with the sign bit. - /// Right-underflowing bits are discarded, i.e. not rotated to the left side. - /// - /// For `y >= 8`, the semantics is the same as for `bitshiftRight(x, y % 8)`. - /// For `y < 0`, the semantics is the same as for `bitshiftRight (x, y + y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitshiftRight(64, 4) == +4 // equivalent to `64 / (2 ** 4)`; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Int8, y : Int8) : Int8 { x >> y }; - - /// Returns the bitwise left rotatation of `x` by `y`, `x <<> y`. - /// Each left-overflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 8`, the semantics is the same as for `bitrotLeft(x, y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitrotLeft(0x11 /* 0b0001_0001 */, 2) == +68 // 0b0100_0100 == 0x44.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Int8, y : Int8) : Int8 { x <<> y }; - - /// Returns the bitwise right rotation of `x` by `y`, `x <>> y`. - /// Each right-underflowing bit is inserted again on the right side. - /// The sign bit is rotated like y bits, i.e. the rotation interprets the number as unsigned. - /// - /// Changes the direction of rotation for negative `y`. - /// For `y >= 8`, the semantics is the same as for `bitrotRight(x, y % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitrotRight(0x11 /* 0b0001_0001 */, 1) == -120 // 0b1000_1000 == 0x88.; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Int8, y : Int8) : Int8 { x <>> y }; - - /// Returns the value of bit `p` in `x`, `x & 2**p == 2**p`. - /// If `p >= 8`, the semantics is the same as for `bittest(x, p % 8)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bittest(64, 6); - /// ``` - public func bittest(x : Int8, p : Nat) : Bool { - Prim.btstInt8(x, Prim.intToInt8(p)) - }; - - /// Returns the value of setting bit `p` in `x` to `1`. - /// If `p >= 8`, the semantics is the same as for `bitset(x, p % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitset(0, 6) == +64; - /// ``` - public func bitset(x : Int8, p : Nat) : Int8 { - x | (1 << Prim.intToInt8(p)) - }; - - /// Returns the value of clearing bit `p` in `x` to `0`. - /// If `p >= 8`, the semantics is the same as for `bitclear(x, p % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitclear(-1, 6) == -65; - /// ``` - public func bitclear(x : Int8, p : Nat) : Int8 { - x & ^(1 << Prim.intToInt8(p)) - }; - - /// Returns the value of flipping bit `p` in `x`. - /// If `p >= 8`, the semantics is the same as for `bitclear(x, p % 8)`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitflip(127, 6) == +63; - /// ``` - public func bitflip(x : Int8, p : Nat) : Int8 { - x ^ (1 << Prim.intToInt8(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitcountNonZero(0x0f) == +4; - /// ``` - public let bitcountNonZero : (x : Int8) -> Int8 = Prim.popcntInt8; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitcountLeadingZero(0x08) == +4; - /// ``` - public let bitcountLeadingZero : (x : Int8) -> Int8 = Prim.clzInt8; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.bitcountTrailingZero(0x10) == +4; - /// ``` - public let bitcountTrailingZero : (x : Int8) -> Int8 = Prim.ctzInt8; - - /// Returns the sum of `x` and `y`, `x +% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.addWrap(2 ** 6, 2 ** 6) == -128; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Int8, y : Int8) : Int8 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.subWrap(-2 ** 7, 1) == +127; // underflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Int8, y : Int8) : Int8 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.mulWrap(2 ** 4, 2 ** 4) == 0; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Int8, y : Int8) : Int8 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. - /// - /// Wraps on overflow/underflow. - /// Traps if `y < 0 or y >= 8`. - /// - /// Example: - /// ```motoko include=import - /// assert Int8.powWrap(2, 7) == -128; // overflow - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Int8, y : Int8) : Int8 { x **% y }; - - /// Returns an iterator over `Int8` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Int8, toExclusive : Int8) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Int8 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Int8` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Int8, to : Int8) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Int8 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Int8 values, from minValue to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Int8.allValues(); - /// assert iter.next() == ?-128; - /// assert iter.next() == ?-127; - /// assert iter.next() == ?-126; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(minValue, maxValue) - }; - -} diff --git a/.mops/core@2.5.0/src/InternetComputer.mo b/.mops/core@2.5.0/src/InternetComputer.mo deleted file mode 100644 index 1a6d618..0000000 --- a/.mops/core@2.5.0/src/InternetComputer.mo +++ /dev/null @@ -1,101 +0,0 @@ -/// Low-level interface to the Internet Computer. - -import Prim "mo:⛔"; - -module { - - /// Calls `canister`'s update or query function, `name`, with the binary contents of `data` as IC argument. - /// Returns the response to the call, an IC _reply_ or _reject_, as a Motoko future: - /// - /// * The message data of an IC reply determines the binary contents of `reply`. - /// * The error code and textual message data of an IC reject determines the future's `Error` value. - /// - /// Note: `call` is an asynchronous function and can only be applied in an asynchronous context. - /// - /// Example: - /// ```motoko no-repl - /// import IC "mo:core/InternetComputer"; - /// import Principal "mo:core/Principal"; - /// - /// persistent actor { - /// type OutputType = { decimals : Nat32 }; - /// - /// public func example() : async ?OutputType { - /// let ledger = Principal.fromText("ryjl3-tyaaa-aaaaa-aaaba-cai"); - /// let method = "decimals"; - /// let input = (); - /// - /// let rawReply = await IC.call(ledger, method, to_candid (input)); // serialized Candid - /// let output : ?OutputType = from_candid (rawReply); - /// assert output == ?{ decimals = 8 }; - /// output - /// } - /// } - /// ``` - /// - /// [Learn more about Candid serialization](https://internetcomputer.org/docs/motoko/language-manual#candid-serialization) - public let call : (canister : Principal, name : Text, data : Blob) -> async (reply : Blob) = Prim.call_raw; - - /// `isReplicated` is true for update messages and for queries that passed through consensus. - public let isReplicated : () -> Bool = Prim.isReplicatedExecution; - - /// Given computation, `comp`, counts the number of actual and (for IC system calls) notional WebAssembly - /// instructions performed during the execution of `comp()`. - /// - /// More precisely, returns the difference between the state of the IC instruction counter (_performance counter_ `0`) before and after executing `comp()` - /// (see [Performance Counter](https://internetcomputer.org/docs/current/references/ic-interface-spec#system-api-performance-counter)). - /// - /// NB: `countInstructions(comp)` will _not_ account for any deferred garbage collection costs incurred by `comp()`. - /// - /// Example: - /// ```motoko no-repl - /// import IC "mo:core/InternetComputer"; - /// - /// let count = IC.countInstructions(func() { - /// // ... - /// }); - /// ``` - public func countInstructions(comp : () -> ()) : Nat64 { - let init = Prim.performanceCounter(0); - let pre = Prim.performanceCounter(0); - comp(); - let post = Prim.performanceCounter(0); - // performance_counter costs around 200 extra instructions; we perform an empty measurement to decide the overhead - let overhead = pre - init; - post - pre - overhead - }; - - /// Returns the current value of IC _performance counter_ `counter`. - /// - /// * Counter `0` is the _current execution instruction counter_, counting instructions only since the beginning of the current IC message. - /// This counter is reset to value `0` on shared function entry and every `await`. - /// It is therefore only suitable for measuring the cost of synchronous code. - /// - /// * Counter `1` is the _call context instruction counter_ for the current shared function call. - /// For replicated message executing, this excludes the cost of nested IC calls (even to the current canister). - /// For non-replicated messages, such as composite queries, it includes the cost of nested calls. - /// The current value of this counter is preserved across `awaits` (unlike counter `0`). - /// - /// * The function (currently) traps if `counter` >= 2. - /// - /// Consult [Performance Counter](https://internetcomputer.org/docs/current/references/ic-interface-spec#system-api-performance-counter) for details. - /// - /// Example: - /// ```motoko no-repl - /// import IC "mo:core/InternetComputer"; - /// - /// let c1 = IC.performanceCounter(1); - /// // ... - /// let diff : Nat64 = IC.performanceCounter(1) - c1; - /// ``` - public let performanceCounter : (counter : Nat32) -> (value : Nat64) = Prim.performanceCounter; - - /// Returns the time (in nanoseconds from the epoch start) by when the update message should - /// reply to the best effort message so that it can be received by the requesting canister. - /// Queries and unbounded-time update messages return null. - public func replyDeadline() : ?Nat { - let raw = Prim.replyDeadline(); - if (raw == 0) null else ?Prim.nat64ToNat(raw) - }; - -} diff --git a/.mops/core@2.5.0/src/Iter.mo b/.mops/core@2.5.0/src/Iter.mo deleted file mode 100644 index c78b99c..0000000 --- a/.mops/core@2.5.0/src/Iter.mo +++ /dev/null @@ -1,869 +0,0 @@ -/// Utilities for `Iter` (iterator) values. -/// -/// Iterators are a way to represent sequences of values that can be lazily produced. -/// They can be used to: -/// - Iterate over collections. -/// - Represent collections that are too large to fit in memory or that are produced incrementally. -/// - Transform collections without creating intermediate collections. -/// -/// Iterators are inherently stateful. Calling `next` "consumes" a value from -/// the Iterator that cannot be put back, so keep that in mind when sharing -/// iterators between consumers. -/// -/// ```motoko name=import -/// import Iter "mo:core/Iter"; -/// ``` -/// -/// -/// An iterator can be iterated over using a `for` loop: -/// ```motoko -/// let iter = [1, 2, 3].values(); -/// for (x in iter) { -/// // do something with x... -/// } -/// ``` -/// -/// Iterators can be: -/// - created from other collections (e.g. using `values` or `keys` function on a `Map`) or from scratch (e.g. using `empty` or `singleton`). -/// - transformed using `map`, `filter`, `concat`, etc. Which can be used to compose several transformations together without materializing intermediate collections. -/// - consumed using `forEach`, `size`, `toArray`, etc. -/// - combined using `concat`. - -import Prim "mo:prim"; - -import Array "Array"; -import Order "Order"; -import Runtime "Runtime"; -import Types "Types"; -import VarArray "VarArray"; - -module { - - /// An iterator that produces values of type `T`. Calling `next` returns - /// `null` when iteration is finished. - /// - /// Iterators are inherently stateful. Calling `next` "consumes" a value from - /// the Iterator that cannot be put back, so keep that in mind when sharing - /// iterators between consumers. - /// - /// An iterator `i` can be iterated over using - /// ```motoko - /// let iter = [1, 2, 3].values(); - /// for (x in iter) { - /// // do something with x... - /// } - /// ``` - public type Iter = Types.Iter; - - /// Creates an empty iterator. - /// - /// ```motoko include=import - /// for (x in Iter.empty()) - /// assert false; // This loop body will never run - /// ``` - public func empty() : Iter { - object { - public func next() : ?T { - null - } - } - }; - - /// Creates an iterator that produces a single value. - /// - /// ```motoko include=import - /// var sum = 0; - /// for (x in Iter.singleton(3)) - /// sum += x; - /// assert sum == 3; - /// ``` - public func singleton(value : T) : Iter { - object { - var state = ?value; - public func next() : ?T { - switch state { - case null null; - case some { - state := null; - some - } - } - } - } - }; - - /// Calls a function `f` on every value produced by an iterator and discards - /// the results. If you're looking to keep these results use `map` instead. - /// - /// ```motoko include=import - /// var sum = 0; - /// Iter.forEach([1, 2, 3].values(), func(x) { - /// sum += x; - /// }); - /// assert sum == 6; - /// ``` - public func forEach( - self : Iter, - f : (T) -> () - ) { - label l loop { - switch (self.next()) { - case (?next) { - f(next) - }; - case (null) { - break l - } - } - } - }; - - /// Takes an iterator and returns a new iterator that pairs each element with its index. - /// The index starts at 0 and increments by 1 for each element. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray(["A", "B", "C"]); - /// let enumerated = Iter.enumerate(iter); - /// let result = Iter.toArray(enumerated); - /// assert result == [(0, "A"), (1, "B"), (2, "C")]; - /// ``` - public func enumerate(self : Iter) : Iter<(Nat, T)> { - object { - var i = 0; - public func next() : ?(Nat, T) { - switch (self.next()) { - case (?x) { - let current = (i, x); - i += 1; - ?current - }; - case null { null } - } - } - } - }; - - /// Creates a new iterator that yields every nth element from the original iterator. - /// If `interval` is 0, returns an empty iterator. If `interval` is 1, returns the original iterator. - /// For any other positive interval, returns an iterator that skips `interval - 1` elements after each yielded element. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5, 6]); - /// let steppedIter = Iter.step(iter, 2); // Take every 2nd element - /// assert ?1 == steppedIter.next(); - /// assert ?3 == steppedIter.next(); - /// assert ?5 == steppedIter.next(); - /// assert null == steppedIter.next(); - /// ``` - public func step(self : Iter, n : Nat) : Iter { - if (n == 0) { - empty() - } else if (n == 1) { - self - } else { - object { - public func next() : ?T { - let item = self.next(); - var i = 1; - while (i < n) { - ignore self.next(); - i += 1 - }; - item - } - } - } - }; - - /// Consumes an iterator and counts how many elements were produced (discarding them in the process). - /// ```motoko include=import - /// let iter = [1, 2, 3].values(); - /// assert 3 == Iter.size(iter); - /// ``` - public func size(self : Iter) : Nat { - var len = 0; - forEach(self, func(x) { len += 1 }); - len - }; - - /// Takes a function and an iterator and returns a new iterator that lazily applies - /// the function to every element produced by the argument iterator. - /// ```motoko include=import - /// let iter = [1, 2, 3].values(); - /// let mappedIter = Iter.map(iter, func (x) = x * 2); - /// let result = Iter.toArray(mappedIter); - /// assert result == [2, 4, 6]; - /// ``` - public func map(self : Iter, f : T -> R) : Iter = object { - public func next() : ?R { - switch (self.next()) { - case (?next) { - ?f(next) - }; - case (null) { - null - } - } - } - }; - - /// Creates a new iterator that only includes elements from the original iterator - /// for which the predicate function returns true. - /// - /// ```motoko include=import - /// let iter = [1, 2, 3, 4, 5].values(); - /// let evenNumbers = Iter.filter(iter, func (x) = x % 2 == 0); - /// let result = Iter.toArray(evenNumbers); - /// assert result == [2, 4]; - /// ``` - public func filter(self : Iter, f : T -> Bool) : Iter = object { - public func next() : ?T { - loop { - let ?x = self.next() else return null; - if (f x) return ?x - }; - null - } - }; - - /// Creates a new iterator by applying a transformation function to each element - /// of the original iterator. Elements for which the function returns null are - /// excluded from the result. - /// - /// ```motoko include=import - /// let iter = [1, 2, 3].values(); - /// let evenNumbers = Iter.filterMap(iter, func (x) = if (x % 2 == 0) ?x else null); - /// let result = Iter.toArray(evenNumbers); - /// assert result == [2]; - /// ``` - public func filterMap(self : Iter, f : T -> ?R) : Iter = object { - public func next() : ?R { - loop { - let ?x = self.next() else return null; - switch (f x) { - case (?r) return ?r; - case null {} // continue - } - } - } - }; - - /// Flattens an iterator of iterators into a single iterator by concatenating the inner iterators. - /// - /// Possible optimization: Use `flatMap` when you need to transform elements before calling `flatten`. Example: use `flatMap(...)` instead of `flatten(map(...))`. - /// ```motoko include=import - /// let iter = Iter.flatten([[1, 2].values(), [3].values(), [4, 5, 6].values()].values()); - /// let result = Iter.toArray(iter); - /// assert result == [1, 2, 3, 4, 5, 6]; - /// ``` - public func flatten(self : Iter>) : Iter = object { - var current : Iter = empty(); - public func next() : ?T { - loop { - switch (current.next()) { - case (?x) return ?x; - case null { - let ?next = self.next() else return null; - current := next - } - } - } - } - }; - - /// Transforms every element of an iterator into an iterator and concatenates the results. - /// ```motoko include=import - /// let iter = Iter.flatMap([1, 3, 5].values(), func (x) = [x, x + 1].values()); - /// let result = Iter.toArray(iter); - /// assert result == [1, 2, 3, 4, 5, 6]; - /// ``` - public func flatMap(self : Iter, f : T -> Iter) : Iter = object { - var current : Iter = empty(); - public func next() : ?R { - loop { - switch (current.next()) { - case (?x) return ?x; - case null { - let ?next = self.next() else return null; - current := f(next) - } - } - } - } - }; - - /// Returns a new iterator that yields at most, first `n` elements from the original iterator. - /// After `n` elements have been produced or the original iterator is exhausted, - /// subsequent calls to `next()` will return `null`. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5]); - /// let first3 = Iter.take(iter, 3); - /// let result = Iter.toArray(first3); - /// assert result == [1, 2, 3]; - /// ``` - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3]); - /// let first5 = Iter.take(iter, 5); - /// let result = Iter.toArray(first5); - /// assert result == [1, 2, 3]; // only 3 elements in the original iterator - /// ``` - public func take(self : Iter, n : Nat) : Iter = object { - var remaining = n; - public func next() : ?T { - if (remaining == 0) return null; - remaining -= 1; - self.next() - } - }; - - /// Returns a new iterator that yields elements from the original iterator until the predicate function returns false. - /// The first element for which the predicate returns false is not included in the result. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5, 4, 3, 2, 1]); - /// let result = Iter.takeWhile(iter, func (x) = x < 4); - /// let array = Iter.toArray(result); - /// assert array == [1, 2, 3]; // note the difference between `takeWhile` and `filter` - /// ``` - public func takeWhile(self : Iter, f : T -> Bool) : Iter = object { - var done = false; - public func next() : ?T { - if done return null; - let ?x = self.next() else return null; - if (f x) return ?x; - done := true; - null - } - }; - - /// Returns a new iterator that skips the first `n` elements from the original iterator. - /// If the original iterator has fewer than `n` elements, the result will be an empty iterator. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5]); - /// let skipped = Iter.drop(iter, 3); - /// let result = Iter.toArray(skipped); - /// assert result == [4, 5]; - /// ``` - public func drop(self : Iter, n : Nat) : Iter = object { - var remaining = n; - public func next() : ?T { - while (remaining > 0) { - let ?_ = self.next() else return null; - remaining -= 1 - }; - self.next() - } - }; - - /// Returns a new iterator that skips elements from the original iterator until the predicate function returns false. - /// The first element for which the predicate returns false is the first element produced by the new iterator. - /// - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3, 4, 5, 4, 3, 2, 1]); - /// let result = Iter.dropWhile(iter, func (x) = x < 4); - /// let array = Iter.toArray(result); - /// assert array == [4, 5, 4, 3, 2, 1]; // notice that `takeWhile` and `dropWhile` are complementary - /// ``` - public func dropWhile(self : Iter, f : T -> Bool) : Iter = object { - var dropping = true; - public func next() : ?T { - while dropping { - let ?x = self.next() else return null; - if (not f x) { - dropping := false; - return ?x - } - }; - self.next() - } - }; - - /// Zips two iterators into a single iterator that produces pairs of elements. - /// The resulting iterator will stop producing elements when either of the input iterators is exhausted. - /// - /// ```motoko include=import - /// let iter1 = [1, 2, 3].values(); - /// let iter2 = ["A", "B"].values(); - /// let zipped = Iter.zip(iter1, iter2); - /// let result = Iter.toArray(zipped); - /// assert result == [(1, "A"), (2, "B")]; // note that the third element from iter1 is not included, because iter2 is exhausted - /// ``` - public func zip(self : Iter, other : Iter) : Iter<(A, B)> = object { - public func next() : ?(A, B) { - let ?x = self.next() else return null; - let ?y = other.next() else return null; - ?(x, y) - } - }; - - /// Zips three iterators into a single iterator that produces triples of elements. - /// The resulting iterator will stop producing elements when any of the input iterators is exhausted. - /// - /// ```motoko include=import - /// let iter1 = ["A", "B"].values(); - /// let iter2 = ["1", "2", "3"].values(); - /// let iter3 = ["x", "y", "z", "xd"].values(); - /// let zipped = Iter.zip3(iter1, iter2, iter3); - /// let result = Iter.toArray(zipped); - /// assert result == [("A", "1", "x"), ("B", "2", "y")]; // note that the unmatched elements from iter2 and iter3 are not included - /// ``` - public func zip3(self : Iter, other1 : Iter, other2 : Iter) : Iter<(A, B, C)> = object { - public func next() : ?(A, B, C) { - let ?x = self.next() else return null; - let ?y = other1.next() else return null; - let ?z = other2.next() else return null; - ?(x, y, z) - } - }; - - /// Zips two iterators into a single iterator by applying a function to zipped pairs of elements. - /// The resulting iterator will stop producing elements when either of the input iterators is exhausted. - /// - /// ```motoko include=import - /// let iter1 = ["A", "B"].values(); - /// let iter2 = ["1", "2", "3"].values(); - /// let zipped = Iter.zipWith(iter1, iter2, func (a, b) = a # b); - /// let result = Iter.toArray(zipped); - /// assert result == ["A1", "B2"]; // note that the third element from iter2 is not included, because iter1 is exhausted - /// ``` - public func zipWith(self : Iter, other : Iter, f : (A, B) -> R) : Iter = object { - public func next() : ?R { - let ?x = self.next() else return null; - let ?y = other.next() else return null; - ?f(x, y) - } - }; - - /// Zips three iterators into a single iterator by applying a function to zipped triples of elements. - /// The resulting iterator will stop producing elements when any of the input iterators is exhausted. - /// - /// ```motoko include=import - /// let iter1 = ["A", "B"].values(); - /// let iter2 = ["1", "2", "3"].values(); - /// let iter3 = ["x", "y", "z", "xd"].values(); - /// let zipped = Iter.zipWith3(iter1, iter2, iter3, func (a, b, c) = a # b # c); - /// let result = Iter.toArray(zipped); - /// assert result == ["A1x", "B2y"]; // note that the unmatched elements from iter2 and iter3 are not included - /// ``` - public func zipWith3(self : Iter, other1 : Iter, other2 : Iter, f : (A, B, C) -> R) : Iter = object { - public func next() : ?R { - let ?x = self.next() else return null; - let ?y = other1.next() else return null; - let ?z = other2.next() else return null; - ?f(x, y, z) - } - }; - - /// Checks if a predicate function is true for all elements produced by an iterator. - /// It stops consuming elements from the original iterator as soon as the predicate returns false. - /// - /// ```motoko include=import - /// assert Iter.all([1, 2, 3].values(), func (x) = x < 4); - /// assert not Iter.all([1, 2, 3].values(), func (x) = x < 3); - /// ``` - public func all(self : Iter, f : T -> Bool) : Bool { - for (x in self) { - if (not f x) return false - }; - true - }; - - /// Checks if a predicate function is true for any element produced by an iterator. - /// It stops consuming elements from the original iterator as soon as the predicate returns true. - /// - /// ```motoko include=import - /// assert Iter.any([1, 2, 3].values(), func (x) = x == 2); - /// assert not Iter.any([1, 2, 3].values(), func (x) = x == 4); - /// ``` - public func any(self : Iter, f : T -> Bool) : Bool { - for (x in self) { - if (f x) return true - }; - false - }; - - /// Finds the first element produced by an iterator for which a predicate function returns true. - /// Returns `null` if no such element is found. - /// It stops consuming elements from the original iterator as soon as the predicate returns true. - /// - /// ```motoko include=import - /// let iter = [1, 2, 3, 4].values(); - /// assert ?2 == Iter.find(iter, func (x) = x % 2 == 0); - /// ``` - public func find(self : Iter, f : T -> Bool) : ?T { - for (x in self) { - if (f x) return ?x - }; - null - }; - - /// Returns the first index in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let iter = ['A', 'B', 'C', 'D'].values(); - /// let found = Iter.findIndex(iter, func(x) { x == 'C' }); - /// assert found == ?2; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func findIndex(self : Iter, predicate : T -> Bool) : ?Nat { - for ((index, element) in enumerate(self)) { - if (predicate element) { - return ?index - } - }; - null - }; - - /// Checks if an element is produced by an iterator. - /// It stops consuming elements from the original iterator as soon as the predicate returns true. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3, 4].values(); - /// assert Iter.contains(iter, Nat.equal, 2); - /// ``` - public func contains(self : Iter, equal : (implicit : (T, T) -> Bool), value : T) : Bool { - for (x in self) { - if (equal(x, value)) return true - }; - false - }; - - /// Reduces an iterator to a single value by applying a function to each element and an accumulator. - /// The accumulator is initialized with the `initial` value. - /// It starts applying the `combine` function starting from the `initial` accumulator value and the first elements produced by the iterator. - /// - /// ```motoko include=import - /// let iter = ["A", "B", "C"].values(); - /// let result = Iter.foldLeft(iter, "S", func (acc, x) = "(" # acc # x # ")"); - /// assert result == "(((SA)B)C)"; - /// ``` - public func foldLeft(self : Iter, initial : R, combine : (R, T) -> R) : R { - var acc = initial; - for (x in self) { - acc := combine(acc, x) - }; - acc - }; - - /// Reduces an iterator to a single value by applying a function to each element in reverse order and an accumulator. - /// The accumulator is initialized with the `initial` value and it is first combined with the last element produced by the iterator. - /// It starts applying the `combine` function starting from the last elements produced by the iterator. - /// - /// **Performance note**: Since this function needs to consume the entire iterator to reverse it, - /// it has to materialize the entire iterator in memory to get to the last element to start applying the `combine` function. - /// **Use `foldLeft` or `reduce` when possible to avoid the extra memory overhead**. - /// - /// ```motoko include=import - /// let iter = ["A", "B", "C"].values(); - /// let result = Iter.foldRight(iter, "S", func (x, acc) = "(" # x # acc # ")"); - /// assert result == "(A(B(CS)))"; - /// ``` - public func foldRight(self : Iter, initial : R, combine : (T, R) -> R) : R { - foldLeft(reverse(self), initial, func(acc, x) = combine(x, acc)) - }; - - /// Reduces an iterator to a single value by applying a function to each element, starting with the first elements. - /// The accumulator is initialized with the first element produced by the iterator. - /// When the iterator is empty, it returns `null`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// assert ?6 == Iter.reduce(iter, Nat.add); - /// ``` - public func reduce(self : Iter, combine : (T, T) -> T) : ?T { - let ?first = self.next() else return null; - ?foldLeft(self, first, combine) - }; - - /// Produces an iterator containing cumulative results of applying the `combine` operator going left to right, including the `initial` value. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// let scanned = Iter.scanLeft(iter, 0, Nat.add); - /// let result = Iter.toArray(scanned); - /// assert result == [0, 1, 3, 6]; - /// ``` - public func scanLeft(self : Iter, initial : R, combine : (R, T) -> R) : Iter = object { - var acc = initial; - var isInitial = true; - public func next() : ?R { - if (isInitial) { - isInitial := false; - return ?acc - }; - switch (self.next()) { - case (?x) { - acc := combine(acc, x); - ?acc - }; - case null null - } - } - }; - - /// Produces an iterator containing cumulative results of applying the `combine` operator going right to left, including the `initial` value. - /// - /// **Performance note**: Since this function needs to consume the entire iterator to reverse it, - /// it has to materialize the entire iterator in memory to get to the last element to start applying the `combine` function. - /// **Use `scanLeft` when possible to avoid the extra memory overhead**. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// let scanned = Iter.scanRight(iter, 0, Nat.add); - /// let result = Iter.toArray(scanned); - /// assert result == [0, 3, 5, 6]; - /// ``` - public func scanRight(self : Iter, initial : R, combine : (T, R) -> R) : Iter { - scanLeft(reverse(self), initial, func(x, acc) = combine(acc, x)) - }; - - /// Creates an iterator that produces elements using the `step` function starting from the `initial` value. - /// The `step` function takes the current state and returns the next element and the next state, or `null` if the iteration is finished. - /// - /// ```motoko include=import - /// let iter = Iter.unfold(1, func (x) = if (x <= 3) ?(x, x + 1) else null); - /// let result = Iter.toArray(iter); - /// assert result == [1, 2, 3]; - /// ``` - public func unfold(initial : S, step : S -> ?(T, S)) : Iter = object { - var state = initial; - public func next() : ?T { - let ?(t, next) = step(state) else return null; - state := next; - ?t - } - }; - - // todo: unfold, iterate, cycle, range, rangeStep, rangeStepTo, rangeStepToExclusive - - /// Consumes an iterator and returns the first maximum element produced by the iterator. - /// If the iterator is empty, it returns `null`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// assert ?3 == Iter.max(iter, Nat.compare); - /// ``` - public func max(self : Iter, compare : (implicit : (T, T) -> Order.Order)) : ?T { - reduce( - self, - func(a, b) { - switch (compare(a, b)) { - case (#less) b; - case _ a - } - } - ) - }; - - /// Consumes an iterator and returns the first minimum element produced by the iterator. - /// If the iterator is empty, it returns `null`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let iter = [1, 2, 3].values(); - /// assert ?1 == Iter.min(iter, Nat.compare); - /// ``` - public func min(self : Iter, compare : (implicit : (T, T) -> Order.Order)) : ?T { - reduce( - self, - func(a, b) { - switch (compare(a, b)) { - case (#greater) b; - case _ a - } - } - ) - }; - - /// Creates an iterator that produces an infinite sequence of `x`. - /// ```motoko include=import - /// let iter = Iter.infinite(10); - /// assert ?10 == iter.next(); - /// assert ?10 == iter.next(); - /// assert ?10 == iter.next(); - /// // ... - /// ``` - public func infinite(item : T) : Iter = object { - public func next() : ?T { - ?item - } - }; - - /// Takes two iterators and returns a new iterator that produces - /// elements from the original iterators sequentally. - /// ```motoko include=import - /// let iter1 = [1, 2].values(); - /// let iter2 = [5, 6, 7].values(); - /// let concatenatedIter = Iter.concat(iter1, iter2); - /// let result = Iter.toArray(concatenatedIter); - /// assert result == [1, 2, 5, 6, 7]; - /// ``` - public func concat(self : Iter, other : Iter) : Iter { - var aEnded : Bool = false; - object { - public func next() : ?T { - if (aEnded) { - return other.next() - }; - switch (self.next()) { - case (?x) ?x; - case (null) { - aEnded := true; - other.next() - } - } - } - } - }; - - /// Creates an iterator that produces the elements of an Array in ascending index order. - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3]); - /// assert ?1 == iter.next(); - /// assert ?2 == iter.next(); - /// assert ?3 == iter.next(); - /// assert null == iter.next(); - /// ``` - /// @deprecated M0235 - public func fromArray(array : [T]) : Iter = array.vals(); - - /// Like `fromArray` but for Arrays with mutable elements. Captures - /// the elements of the Array at the time the iterator is created, so - /// further modifications won't be reflected in the iterator. - /// @deprecated M0235 - public func fromVarArray(array : [var T]) : Iter = array.vals(); - - /// Consumes an iterator and collects its produced elements in an Array. - /// ```motoko include=import - /// let iter = [1, 2, 3].values(); - /// assert [1, 2, 3] == Iter.toArray(iter); - /// ``` - public func toArray(self : Iter) : [T] { - // TODO: Replace implementation. This is just temporay. - type Node = { value : T; var next : ?Node }; - var first : ?Node = null; - var last : ?Node = null; - var count = 0; - - func add(value : T) { - let node : Node = { value; var next = null }; - switch (last) { - case null { - first := ?node - }; - case (?previous) { - previous.next := ?node - } - }; - last := ?node; - count += 1 - }; - - for (value in self) { - add(value) - }; - if (count == 0) { - return [] - }; - var current = first; - Prim.Array_tabulate( - count, - func(_) { - switch (current) { - case null Runtime.trap("Iter.toArray(): node must not be null"); - case (?node) { - current := node.next; - node.value - } - } - } - ) - }; - - /// Like `toArray` but for Arrays with mutable elements. - public func toVarArray(self : Iter) : [var T] { - Array.toVarArray(toArray(self)) - }; - - /// Sorted iterator. Will iterate over *all* elements to sort them, necessarily. - public func sort(self : Iter, compare : (implicit : (T, T) -> Order.Order)) : Iter { - let array = toVarArray(self); - VarArray.sortInPlace(array, compare); - fromVarArray(array) - }; - - /// Creates an iterator that produces a given item a specified number of times. - /// ```motoko include=import - /// let iter = Iter.repeat(3, 2); - /// assert ?3 == iter.next(); - /// assert ?3 == iter.next(); - /// assert null == iter.next(); - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func repeat(item : T, count : Nat) : Iter = object { - var remaining = count; - public func next() : ?T { - if (remaining == 0) { - null - } else { - remaining -= 1; - ?item - } - } - }; - - /// Creates a new iterator that produces elements from the original iterator in reverse order. - /// Note: This function needs to consume the entire iterator to reverse it. - /// ```motoko include=import - /// let iter = Iter.fromArray([1, 2, 3]); - /// let reversed = Iter.reverse(iter); - /// assert ?3 == reversed.next(); - /// assert ?2 == reversed.next(); - /// assert ?1 == reversed.next(); - /// assert null == reversed.next(); - /// ``` - /// - /// Runtime: O(n) where n is the number of elements in the iterator - /// - /// Space: O(n) where n is the number of elements in the iterator - public func reverse(self : Iter) : Iter { - var acc : Types.Pure.List = null; - for (x in self) { - acc := ?(x, acc) - }; - object { - public func next() : ?T { - switch acc { - case null null; - case (?(h, t)) { - acc := t; - ?h - } - } - } - } - }; - -} diff --git a/.mops/core@2.5.0/src/List.mo b/.mops/core@2.5.0/src/List.mo deleted file mode 100644 index 07f98eb..0000000 --- a/.mops/core@2.5.0/src/List.mo +++ /dev/null @@ -1,3138 +0,0 @@ -/// A mutable growable array data structure with efficient random access and dynamic resizing. -/// `List` provides O(1) access time and O(sqrt(n)) memory overhead. In contrast, `pure/List` is a purely functional linked list. -/// Can be declared `stable` for orthogonal persistence. -/// -/// This implementation is adapted with permission from the `vector` Mops package created by Research AG. -/// -/// Copyright: 2023 MR Research AG -/// Main author: Andrii Stepanov (AStepanov25) -/// Contributors: Timo Hanke (timohanke), Andy Gura (andygura), react0r-com -/// -/// ```motoko name=import -/// import List "mo:core/List"; -/// ``` - -import PureList "pure/List"; -import Prim "mo:⛔"; -import Nat32 "Nat32"; -import Array "Array"; -import Nat "Nat"; -import Option "Option"; -import VarArray "VarArray"; -import Types "Types"; - -module { - /// `List` provides a mutable list of elements of type `T`. - /// Based on the paper "Resizable Arrays in Optimal Time and Space" by Brodnik, Carlsson, Demaine, Munro and Sedgewick (1999). - /// Since this is internally a two-dimensional array the access times for put and get operations - /// will naturally be 2x slower than Buffer and Array. However, Array is not resizable and Buffer - /// has `O(size)` memory waste. - /// - /// The maximum number of elements in a `List` is 2^32. - public type List = Types.List; - - let INTERNAL_ERROR = "List: internal error"; - - /// Creates a new empty List for elements of type T. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); // Creates a new List - /// ``` - public func empty() : List = { - // the first block is always empty and is present in each List - // this is done to optimize locate, at, get, etc - var blocks = [var [var]]; - // can't be 0 in any List - var blockIndex = 1; - var elementIndex = 0 - }; - - /// Returns a new list with capacity and size 1, containing `element`. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.singleton(1); - /// assert List.toText(list, Nat.toText) == "List[1]"; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func singleton(element : T) : List = { - var blockIndex = 2; - var blocks = [var [var], [var ?element]]; - var elementIndex = 0 - }; - - func repeatInternal(initValue : ?T, size : Nat) : List { - let (blockIndex, elementIndex) = locate(size); - - let blocks = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) { blockIndex - 1 } else blockIndex)); - let dataBlocks = VarArray.repeat<[var ?T]>([var], blocks); - var i = 1; - while (i < blockIndex) { - dataBlocks[i] := VarArray.repeat(initValue, dataBlockSize(i)); - i += 1 - }; - if (elementIndex != 0) { - dataBlocks[blockIndex] := if (Option.isNull(initValue)) VarArray.repeat( - null, - dataBlockSize(blockIndex) - ) else VarArray.tabulate( - dataBlockSize(blockIndex), - func i = if (i < elementIndex) initValue else null - ) - }; - - { - var blocks = dataBlocks; - var blockIndex = blockIndex; - var elementIndex = elementIndex - } - }; - - /// Creates a new List with `size` copies of the initial value. - /// - /// Example: - /// ```motoko include=import - /// let list = List.repeat(2, 4); - /// assert List.toArray(list) == [2, 2, 2, 2]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func repeat(initValue : T, size : Nat) : List = repeatInternal(?initValue, size); - - /// Fills all elements in the list with the given value. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3]); - /// List.fill(list, 0); // fills the list with 0 - /// assert List.toArray(list) == [0, 0, 0]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func fill(self : List, value : T) { - let blocks = self.blocks; - let blockCount = blocks.size(); - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = if (i == blockIndex) elementIndex else db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - db[j] := ?value; - j += 1 - }; - i += 1 - } - }; - - /// Converts a mutable `List` to a purely functional `PureList`. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3]); - /// let pureList = List.toPure(list); // converts to immutable PureList - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// @deprecated M0235 - public func toPure(self : List) : PureList.List { - var result : PureList.List = null; - - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) result := ?(x, result); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - }; - - result - }; - - /// Converts a purely functional `PureList` to a `List`. - /// - /// Example: - /// ```motoko include=import - /// import PureList "mo:core/pure/List"; - /// - /// let pureList = PureList.fromArray([1, 2, 3]); - /// let list = List.fromPure(pureList); // converts to List - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// @deprecated M0235 - public func fromPure(pure : PureList.List) : List { - var p = pure; - var list = empty(); - loop { - switch (p) { - case (?(x, xs)) { - add(list, x); - p := xs - }; - case null return list - } - } - }; - - func addRepeatInternal(list : List, initValue : ?T, count : Nat) { - let (b, e) = locate(size(list) + count); - let blocksCount = newIndexBlockLength(Nat32.fromNat(if (e == 0) b - 1 else b)); - - let oldBlocksCount = list.blocks.size(); - if (oldBlocksCount < blocksCount) { - let oldBlocks = list.blocks; - let blocks = VarArray.repeat<[var ?T]>([var], blocksCount); - var i = 0; - while (i < oldBlocksCount) { - blocks[i] := oldBlocks[i]; - i += 1 - }; - list.blocks := blocks - }; - - let blocks = list.blocks; - var blockIndex = list.blockIndex; - var elementIndex = list.elementIndex; - - var cnt = count; - label L while (cnt > 0) { - if (blocks[blockIndex].size() == 0) { - let dbSize = dataBlockSize(blockIndex); - if (cnt >= dbSize) { - blocks[blockIndex] := VarArray.repeat(initValue, dbSize); - blockIndex += 1; - cnt -= dbSize; - continue L - }; - blocks[blockIndex] := VarArray.repeat(null, dbSize) - }; - - let block = blocks[blockIndex]; - let dbSize = block.size(); - let to = Nat.min(elementIndex + cnt, dbSize); - cnt -= to - elementIndex; - - while (elementIndex < to) { - block[elementIndex] := initValue; - elementIndex += 1 - }; - - if (elementIndex == dbSize) { - elementIndex := 0; - blockIndex += 1 - } - }; - - list.blockIndex := blockIndex; - list.elementIndex := elementIndex - }; - - private func reserve(list : List, size : Nat) { - let blockIndex = list.blockIndex; - let elementIndex = list.elementIndex; - - addRepeatInternal(list, null, size); - - list.blockIndex := blockIndex; - list.elementIndex := elementIndex - }; - - /// Add to list `count` copies of the initial value. - /// - /// ```motoko include=import - /// let list = List.repeat(2, 4); // [2, 2, 2, 2] - /// List.addRepeat(list, 2, 1); // [2, 2, 2, 2, 1, 1] - /// ``` - /// - /// The maximum number of elements in a `List` is 2^32. - /// - /// Runtime: `O(count)` - public func addRepeat(self : List, initValue : T, count : Nat) = addRepeatInternal(self, ?initValue, count); - - /// Truncates the list to the specified size. - /// If the new size is larger than the current size, it will do nothing. - /// If the new size is equal to the current list size, after the operation list will be equal to cloned version of itself. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4, 5]); - /// List.truncate(list, 3); // list is now [1, 2, 3] - /// assert List.toArray(list) == [1, 2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func truncate(self : List, newSize : Nat) { - if (newSize > size(self)) return; - - // if newSize == size(self) then after the operation self will be equal to List.clone(self) - let (blockIndex, elementIndex) = locate(newSize); - self.blockIndex := blockIndex; - self.elementIndex := elementIndex; - let newBlocksCount = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) blockIndex - 1 else blockIndex)); - - let newBlocks = if (newBlocksCount < self.blocks.size()) { - let oldDataBlocks = self.blocks; - self.blocks := VarArray.tabulate<[var ?T]>(newBlocksCount, func(i) = oldDataBlocks[i]); - self.blocks - } else self.blocks; - - var i = if (elementIndex == 0) blockIndex else blockIndex + 1; - while (i < newBlocksCount) { - newBlocks[i] := [var]; - i += 1 - }; - if (elementIndex != 0) { - let block = newBlocks[blockIndex]; - var i = elementIndex; - var to = block.size(); - while (i < to) { - block[i] := null; - i += 1 - } - } - }; - - /// Resets the list to size 0, de-referencing all elements. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// List.clear(list); // list is now empty - /// assert List.toArray(list) == []; - /// ``` - /// - /// Runtime: `O(1)` - public func clear(self : List) { - self.blocks := [var [var]]; - self.blockIndex := 1; - self.elementIndex := 0 - }; - - /// Creates a list of size `size`. Each element at index i - /// is created by applying `generator` to i. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.tabulate(4, func i = i * 2); - /// assert List.toArray(list) == [0, 2, 4, 6]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `generator` runs in O(1) time and space. - public func tabulate(size : Nat, generator : Nat -> T) : List { - let (blockIndex, elementIndex) = locate(size); - - let blocks = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) { blockIndex - 1 } else blockIndex)); - let dataBlocks = VarArray.repeat<[var ?T]>([var], blocks); - - var i = 1; - var pos = 0; - - while (i < blockIndex) { - let len = dataBlockSize(i); - dataBlocks[i] := VarArray.tabulate(len, func i = ?generator(pos + i)); - pos += len; - i += 1 - }; - if (elementIndex != 0 and blockIndex < blocks) { - dataBlocks[i] := VarArray.tabulate( - dataBlockSize(blockIndex), - func i = if (i < elementIndex) ?generator(pos + i) else null - ) - }; - - { - var blocks = dataBlocks; - var blockIndex = blockIndex; - var elementIndex = elementIndex - } - }; - - /// Combines a list of lists into a single list. Retains the original - /// ordering of the elements. - /// - /// This has better performance compared to `List.join()`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let lists = List.fromArray>([ - /// List.fromArray([0, 1, 2]), List.fromArray([2, 3]), List.fromArray([]), List.fromArray([4]) - /// ]); - /// let flatList = List.flatten(lists); - /// assert List.equal(flatList, List.fromArray([0, 1, 2, 2, 3, 4]), Nat.equal); - /// ``` - /// - /// Runtime: O(number of elements in list) - /// - /// Space: O(number of elements in list) - public func flatten(self : List>) : List { - var sz = 0; - forEach>(self, func(sublist) = sz += size(sublist)); - - let result = repeatInternal(null, sz); - result.blockIndex := 1; - result.elementIndex := 0; - - forEach>( - self, - func(sublist) { - forEach( - sublist, - func(item) { - add(result, item) - } - ) - } - ); - result - }; - - /// Combines an iterator of lists into a single list. - /// Retains the original ordering of the elements. - /// - /// Consider using `List.flatten()` for better performance. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let lists = [List.fromArray([0, 1, 2]), List.fromArray([2, 3]), List.fromArray([]), List.fromArray([4])]; - /// let joinedList = List.join(lists.vals()); - /// assert List.equal(joinedList, List.fromArray([0, 1, 2, 2, 3, 4]), Nat.equal); - /// ``` - /// - /// Runtime: O(number of elements in list) - /// - /// Space: O(number of elements in list) - public func join(self : Types.Iter>) : List { - var result = empty(); - for (list in self) { - reserve(result, size(list)); - forEach(list, func item = addUnsafe(result, item)) - }; - result - }; - - /// Returns a copy of a List, with the same size. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 1); - /// - /// let clone = List.clone(list); - /// assert List.toArray(clone) == [1]; - /// ``` - /// - /// Runtime: `O(size)` - public func clone(self : List) : List = { - var blocks = VarArray.tabulate<[var ?T]>( - Nat.min( - newIndexBlockLength(Nat32.fromNat(if (self.elementIndex == 0) self.blockIndex - 1 else self.blockIndex)), - self.blocks.size() - ), - func(i) = VarArray.clone(self.blocks[i]) - ); - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - }; - - /// Creates a new list by applying the provided function to each element in the input list. - /// The resulting list has the same size as the input list. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.singleton(123); - /// let textList = List.map(list, Nat.toText); - /// assert List.toArray(textList) == ["123"]; - /// ``` - /// - /// Runtime: `O(size)` - public func map(self : List, f : T -> R) : List { - let blocksCount = Nat.min( - newIndexBlockLength(Nat32.fromNat(if (self.elementIndex == 0) self.blockIndex - 1 else self.blockIndex)), - self.blocks.size() - ); - let blocks = VarArray.repeat<[var ?R]>([var], blocksCount); - - var i = 1; - label l while (i < blocksCount) { - let oldBlock = self.blocks[i]; - let blockSize = oldBlock.size(); - let newBlock = VarArray.repeat(null, blockSize); - blocks[i] := newBlock; - var j = 0; - - while (j < blockSize) { - switch (oldBlock[j]) { - case (?item) newBlock[j] := ?f(item); - case null break l - }; - j += 1 - }; - i += 1 - }; - - { - var blocks = blocks; - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - } - }; - - /// Applies `f` to each element of `list` in place, - /// retaining the original ordering of elements. - /// This modifies the original list. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([0, 1, 2, 3]); - /// List.mapInPlace(list, func x = x * 3); - /// assert List.equal(list, List.fromArray([0, 3, 6, 9]), Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapInPlace(self : List, f : T -> T) { - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) db[j] := ?f(x); - case null return - }; - j += 1 - }; - i += 1 - } - }; - - /// Creates a new list by applying `f` to each element in `list` and its index. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([10, 10, 10, 10]); - /// let newList = List.mapEntries(list, func (x, i) = i * x); - /// assert List.equal(newList, List.fromArray([0, 10, 20, 30]), Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapEntries(self : List, f : (T, Nat) -> R) : List { - let blocks = VarArray.repeat<[var ?R]>([var], self.blocks.size()); - let blocksCount = self.blocks.size(); - - var index = 0; - - var i = 1; - label l while (i < blocksCount) { - let oldBlock = self.blocks[i]; - let blockSize = oldBlock.size(); - let newBlock = VarArray.repeat(null, blockSize); - blocks[i] := newBlock; - var j = 0; - - while (j < blockSize) { - switch (oldBlock[j]) { - case (?item) newBlock[j] := ?f(item, index); - case null break l - }; - j += 1; - index += 1 - }; - i += 1 - }; - - { - var blocks = blocks; - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - } - }; - - /// Creates a new list by applying `f` to each element in `list`. - /// If any invocation of `f` produces an `#err`, returns an `#err`. Otherwise - /// returns an `#ok` containing the new list. - /// - /// ```motoko include=import - /// import Result "mo:core/Result"; - /// - /// let list = List.fromArray([4, 3, 2, 1, 0]); - /// // divide 100 by every element in the list - /// let result = List.mapResult(list, func x { - /// if (x > 0) { - /// #ok(100 / x) - /// } else { - /// #err "Cannot divide by zero" - /// } - /// }); - /// assert Result.isErr(result); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapResult(self : List, f : T -> Types.Result) : Types.Result, E> { - var error : ?E = null; - - let blocks = VarArray.repeat<[var ?R]>([var], self.blocks.size()); - let blocksCount = self.blocks.size(); - - var i = 1; - while (i < blocksCount) { - let oldBlock = self.blocks[i]; - let blockSize = oldBlock.size(); - let newBlock = VarArray.repeat(null, blockSize); - blocks[i] := newBlock; - var j = 0; - - while (j < blockSize) { - switch (oldBlock[j]) { - case (?item) newBlock[j] := switch (f(item)) { - case (#ok x) ?x; - case (#err e) switch (error) { - case (null) { - error := ?e; - null - }; - case (?_) null - } - }; - case null return switch (error) { - case (null) return #ok { - var blocks = blocks; - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - }; - case (?e) return #err e - } - }; - j += 1 - }; - i += 1 - }; - - switch (error) { - case (null) return #ok { - var blocks = blocks; - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex - }; - case (?e) return #err e - } - }; - - /// Returns a new list containing only the elements from `list` for which the predicate returns true. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4]); - /// let evenNumbers = List.filter(list, func x = x % 2 == 0); - /// assert List.toArray(evenNumbers) == [2, 4]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func filter(self : List, predicate : T -> Bool) : List { - let filtered = empty(); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return filtered; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (predicate(x)) add(filtered, x); - case null return filtered - }; - j += 1 - }; - i += 1 - }; - - filtered - }; - - /// Retains only the elements in `list` for which the predicate returns true. - /// Modifies the original list in place. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4]); - /// List.retain(list, func x = x % 2 == 0); - /// assert List.toArray(list) == [2, 4]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(sqrt(size))` if `list` was truncated otherwise `O(1)` - public func retain(self : List, predicate : T -> Bool) { - self.blockIndex := 1; - self.elementIndex := 0; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - label l while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) break l; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (predicate(x)) addUnsafe(self, x); - case null break l - }; - j += 1 - }; - i += 1 - }; - - truncate(self, size(self)) - }; - - /// Returns a new list containing all elements from `list` for which the function returns ?element. - /// Discards all elements for which the function returns null. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4]); - /// let doubled = List.filterMap(list, func x = if (x % 2 == 0) ?(x * 2) else null); - /// assert List.toArray(doubled) == [4, 8]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func filterMap(self : List, f : T -> ?R) : List { - let filtered = empty(); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return filtered; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) switch (f(x)) { - case (?y) add(filtered, y); - case null {} - }; - case null return filtered - }; - j += 1 - }; - i += 1 - }; - - filtered - }; - - /// Creates a new list by applying `k` to each element in `list`, - /// and concatenating the resulting iterators in order. - /// - /// ```motoko include=import - /// import Int "mo:core/Int" - /// - /// let list = List.fromArray([1, 2, 3, 4]); - /// let newList = List.flatMap(list, func x = [x, -x].vals()); - /// assert List.equal(newList, List.fromArray([1, -1, 2, -2, 3, -3, 4, -4]), Int.equal); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `k` runs in O(1) time and space. - public func flatMap(self : List, k : T -> Types.Iter) : List { - let result = empty(); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return result; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) for (y in k(x)) add(result, y); - case _ return result - }; - j += 1 - }; - i += 1 - }; - - result - }; - - func indexByBlockElement(blockIndex : Nat, elementIndex : Nat) : Nat { - let d = Nat32.fromNat(blockIndex); - - // We call all data blocks of the same capacity an "epoch". We number the epochs 0,1,2,... - // A data block is in epoch e iff the data block has capacity 2 ** e. - // Each epoch starting with epoch 1 spans exactly two super blocks. - // Super block s falls in epoch ceil(s/2). - - // epoch of last data block - // e = 32 - lz - let lz = Nat32.bitcountLeadingZero(d / 3); - - // capacity of all prior epochs combined - // capacity_before_e = 2 * 4 ** (e - 1) - 1 - - // data blocks in all prior epochs combined - // blocks_before_e = 3 * 2 ** (e - 1) - 2 - - // then size = d * 2 ** e + i - c - // where c = blocks_before_e * 2 ** e - capacity_before_e - - // there can be overflows, but the result is without overflows, so use addWrap and subWrap - // we don't erase bits by >>, so to use <>> is ok - Nat32.toNat((d -% (1 <>> lz)) <>> lz +% Nat32.fromNat(elementIndex)) - }; - - /// Returns the current number of elements in the list. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// assert List.size(list) == 0 - /// ``` - /// - /// Runtime: `O(1)` (with some internal calculations) - public func size(self : List) : Nat { - // due to the design of List (blockIndex, elementIndex) pair points - // exactly to the place where size-th element should be added - // so, it's the inlined version of indexByBlockElement - let d = Nat32.fromNat(self.blockIndex); - let lz = Nat32.bitcountLeadingZero(d / 3); - Nat32.toNat((d -% (1 <>> lz)) <>> lz +% Nat32.fromNat(self.elementIndex)) - }; - - func dataBlockSize(blockIndex : Nat) : Nat { - // formula for the size of given blockIndex - // don't call it for blockIndex == 0 - Nat32.toNat(1 <>> Nat32.bitcountLeadingZero(Nat32.fromNat(blockIndex) / 3)) - }; - - func newIndexBlockLength(blockIndex : Nat32) : Nat { - if (blockIndex <= 1) 2 else { - let s = 30 - Nat32.bitcountLeadingZero(blockIndex); - Nat32.toNat(((blockIndex >> s) +% 1) << s) - } - }; - - func growIndexBlockIfNeeded(list : List) { - if (list.blocks.size() == list.blockIndex) { - let newBlocks = VarArray.repeat<[var ?T]>([var], newIndexBlockLength(Nat32.fromNat(list.blockIndex))); - var i = 0; - while (i < list.blockIndex) { - newBlocks[i] := list.blocks[i]; - i += 1 - }; - list.blocks := newBlocks - } - }; - - func shrinkIndexBlockIfNeeded(list : List) { - let blockIndex = Nat32.fromNat(list.blockIndex); - // kind of index of the first block in the super block - if ((blockIndex << Nat32.bitcountLeadingZero(blockIndex)) << 2 == 0) { - let newLength = newIndexBlockLength(blockIndex); - if (newLength < list.blocks.size()) { - let newBlocks = VarArray.repeat<[var ?T]>([var], newLength); - var i = 0; - while (i < newLength) { - newBlocks[i] := list.blocks[i]; - i += 1 - }; - list.blocks := newBlocks - } - } - }; - - /// Adds a single element to the end of a List, - /// allocating a new internal data block if needed, - /// and resizing the internal index block if needed. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 0); // add 0 to list - /// List.add(list, 1); - /// List.add(list, 2); - /// List.add(list, 3); - /// assert List.toArray(list) == [0, 1, 2, 3]; - /// ``` - /// - /// The maximum number of elements in a `List` is 2^32. - /// - /// Amortized Runtime: `O(1)`, Worst Case Runtime: `O(sqrt(n))` - public func add(self : List, element : T) { - var elementIndex = self.elementIndex; - if (elementIndex == 0) { - growIndexBlockIfNeeded(self); - let blockIndex = self.blockIndex; - - // When removing last we keep one more data block, so can be not empty - if (self.blocks[blockIndex].size() == 0) { - self.blocks[blockIndex] := VarArray.repeat( - null, - dataBlockSize(blockIndex) - ) - } - }; - - let lastDataBlock = self.blocks[self.blockIndex]; - - lastDataBlock[elementIndex] := ?element; - - elementIndex += 1; - if (elementIndex == lastDataBlock.size()) { - elementIndex := 0; - self.blockIndex += 1 - }; - self.elementIndex := elementIndex - }; - - // Add an element without checking and resizing the List - private func addUnsafe(list : List, element : T) { - var elementIndex = list.elementIndex; - let lastDataBlock = list.blocks[list.blockIndex]; - lastDataBlock[elementIndex] := ?element; - - elementIndex += 1; - if (elementIndex == lastDataBlock.size()) { - elementIndex := 0; - list.blockIndex += 1 - }; - list.elementIndex := elementIndex - }; - - /// Removes and returns the last item in the list or `null` if - /// the list is empty. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// assert List.removeLast(list) == ?11; - /// assert List.removeLast(list) == ?10; - /// assert List.removeLast(list) == null; - /// ``` - /// - /// Amortized Runtime: `O(1)`, Worst Case Runtime: `O(sqrt(n))` - /// - /// Amortized Space: `O(1)`, Worst Case Space: `O(sqrt(n))` - public func removeLast(self : List) : ?T { - var elementIndex = self.elementIndex; - if (elementIndex == 0) { - var blockIndex = self.blockIndex; - if (blockIndex == 1) { - return null - }; - - shrinkIndexBlockIfNeeded(self); - - blockIndex -= 1; - elementIndex := self.blocks[blockIndex].size(); - - // Keep one totally empty block when removing - if (blockIndex + 2 < self.blocks.size()) self.blocks[blockIndex + 2] := [var]; - - self.blockIndex := blockIndex - }; - elementIndex -= 1; - - let lastDataBlock = self.blocks[self.blockIndex]; - - let element = lastDataBlock[elementIndex]; - lastDataBlock[elementIndex] := null; - - self.elementIndex := elementIndex; - return element - }; - - func locate(index : Nat) : (Nat, Nat) { - // see comments in tests - let i = Nat32.fromNat(index); - let lz = Nat32.bitcountLeadingZero(i); - let lz2 = lz >> 1; - if (lz & 1 == 0) { - (Nat32.toNat(((i << lz2) >> 16) ^ (0x10000 >> lz2)), Nat32.toNat(i & (0xFFFF >> lz2))) - } else { - (Nat32.toNat(((i << lz2) >> 15) ^ (0x18000 >> lz2)), Nat32.toNat(i & (0x7FFF >> lz2))) - } - }; - - /// Returns the element at index `index`. Indexing is zero-based. - /// Traps if `index >= size`, error message may not be descriptive. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// assert List.at(list, 0) == 10; - /// ``` - /// - /// Runtime: `O(1)` - public func at(self : List, index : Nat) : T { - // inlined version of: - // let (a,b) = locate(index); - // switch(self.blocks[a][b]) { - // case (?element) element; - // case (null) Prim.trap ""; - // }; - let i = Nat32.fromNat(index); - let lz = Nat32.bitcountLeadingZero(i); - let lz2 = lz >> 1; - switch ( - if (lz & 1 == 0) { - self.blocks[Nat32.toNat(((i << lz2) >> 16) ^ (0x10000 >> lz2))][Nat32.toNat(i & (0xFFFF >> lz2))] - } else { - self.blocks[Nat32.toNat(((i << lz2) >> 15) ^ (0x18000 >> lz2))][Nat32.toNat(i & (0x7FFF >> lz2))] - } - ) { - case (?result) return result; - case (_) Prim.trap "List index out of bounds in get" - } - }; - - /// Returns the element at index `index` as an option. - /// Returns `null` when `index >= size`. Indexing is zero-based. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// assert List.get(list, 0) == ?10; - /// assert List.get(list, 2) == null; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - /// @deprecated M0235 - public func get(self : List, index : Nat) : ?T { - // inlined version of locate - let (a, b) = do { - let i = Nat32.fromNat(index); - let lz = Nat32.bitcountLeadingZero(i); - let lz2 = lz >> 1; - if (lz & 1 == 0) { - (Nat32.toNat(((i << lz2) >> 16) ^ (0x10000 >> lz2)), Nat32.toNat(i & (0xFFFF >> lz2))) - } else { - (Nat32.toNat(((i << lz2) >> 15) ^ (0x18000 >> lz2)), Nat32.toNat(i & (0x7FFF >> lz2))) - } - }; - if (a < self.blockIndex or self.elementIndex != 0 and a == self.blockIndex) { - self.blocks[a][b] - } else null - }; - - /// Overwrites the current element at `index` with `element`. - /// Traps if `index` >= size, error message may not be descriptive. Indexing is zero-based. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.put(list, 0, 20); // overwrites 10 at index 0 with 20 - /// assert List.toArray(list) == [20]; - /// ``` - /// - /// Runtime: `O(1)` - public func put(self : List, index : Nat, value : T) { - let i = Nat32.fromNat(index); - let lz = Nat32.bitcountLeadingZero(i); - let lz2 = lz >> 1; - let (block, element) = if (lz & 1 == 0) { - (self.blocks[Nat32.toNat(((i << lz2) >> 16) ^ (0x10000 >> lz2))], Nat32.toNat(i & (0xFFFF >> lz2))) - } else { - (self.blocks[Nat32.toNat(((i << lz2) >> 15) ^ (0x18000 >> lz2))], Nat32.toNat(i & (0x7FFF >> lz2))) - }; - - switch (block[element]) { - case (?_) block[element] := ?value; - case _ Prim.trap "List index out of bounds in put" - } - }; - - /// Sorts the elements in the list according to `compare`. - /// Sort is deterministic, stable, and in-place. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 3); - /// List.add(list, 1); - /// List.add(list, 2); - /// List.sortInPlace(list, Nat.compare); - /// assert List.toArray(list) == [1, 2, 3]; - /// ``` - /// - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sortInPlace(self : List, compare : (implicit : (T, T) -> Types.Order)) { - if (size(self) < 2) return; - let array = toVarArray(self); - - VarArray.sortInPlace(array, compare); - - var index = 0; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?_) db[j] := ?array[index]; - case _ return - }; - index += 1; - j += 1 - }; - i += 1 - } - }; - - /// Sorts the elements in the list according to `compare`. - /// Sort is deterministic, stable, and in-place. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 3); - /// List.add(list, 1); - /// List.add(list, 2); - /// let sorted = List.sort(list, Nat.compare); - /// assert List.toArray(sorted) == [1, 2, 3]; - /// ``` - /// - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sort(self : List, compare : (implicit : (T, T) -> Types.Order)) : List { - let array = toVarArray(self); - VarArray.sortInPlace(array, compare); - fromVarArray(array) - }; - - /// Checks whether the `list` is sorted. - /// - /// Example: - /// ``` - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// assert List.isSorted(list, Nat.compare); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func isSorted(self : List, compare : (implicit : (T, T) -> Types.Order)) : Bool { - var prev = switch (first(self)) { - case (?x) x; - case _ return true - }; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return true; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) switch (compare(x, prev)) { - case (#greater or #equal) prev := x; - case (#less) return false - }; - case null return true - }; - j += 1 - }; - i += 1 - }; - - true - }; - - /// Remove adjacent duplicates from the `list`, if the `list` is sorted all elements will be unique. - /// - /// Example: - /// ``` - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 1, 2, 2, 3]); - /// List.deduplicate(list, Nat.equal); - /// assert List.equal(list, List.fromArray([1, 2, 3]), Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func deduplicate(self : List, equal : (implicit : (T, T) -> Bool)) { - var prev = switch (first(self)) { - case (?x) x; - case _ return - }; - - self.blockIndex := 1; - self.elementIndex := 0; - - addUnsafe(self, prev); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - label l while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return break l; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) { - if (not equal(x, prev)) addUnsafe(self, x); - prev := x - }; - case null break l - }; - j += 1 - }; - i += 1 - }; - - truncate(self, size(self)) - }; - - /// Finds the first index of `element` in `list` using equality of elements defined - /// by `equal`. Returns `null` if `element` is not found. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.indexOf(list, Nat.equal, 3) == ?2; - /// assert List.indexOf(list, Nat.equal, 5) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// *Runtime and space assumes that `equal` runs in `O(1)` time and space. - public func indexOf(self : List, equal : (implicit : (T, T) -> Bool), element : T) : ?Nat { - if (isEmpty(self)) return null; - nextIndexOf(self, equal, element, 0) - }; - - /// Returns the index of the next occurence of `element` in the `list` starting from the `from` index (inclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let list = List.fromArray(['c', 'o', 'f', 'f', 'e', 'e']); - /// assert List.nextIndexOf(list, Char.equal, 'c', 0) == ?0; - /// assert List.nextIndexOf(list, Char.equal, 'f', 0) == ?2; - /// assert List.nextIndexOf(list, Char.equal, 'f', 2) == ?2; - /// assert List.nextIndexOf(list, Char.equal, 'f', 3) == ?3; - /// assert List.nextIndexOf(list, Char.equal, 'f', 4) == null; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func nextIndexOf(self : List, equal : (implicit : (T, T) -> Bool), element : T, fromInclusive : Nat) : ?Nat { - if (fromInclusive >= size(self)) Prim.trap "List index out of bounds in nextIndexOf"; - - let (blockIndex, elementIndex) = locate(fromInclusive); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = blockIndex; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return null; - - var j = if (i == blockIndex) elementIndex else 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (equal(x, element)) return ?indexByBlockElement(i, j); - case null return null - }; - j += 1 - }; - i += 1 - }; - null - }; - - /// Finds the last index of `element` in `list` using equality of elements defined - /// by `equal`. Returns `null` if `element` is not found. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 2, 3, 4, 2, 2]); - /// - /// assert List.lastIndexOf(list, Nat.equal, 2) == ?5; - /// assert List.lastIndexOf(list, Nat.equal, 5) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// *Runtime and space assumes that `equal` runs in `O(1)` time and space. - public func lastIndexOf(self : List, equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = prevIndexOf( - self, - equal, - element, - size(self) - ); - - /// Returns the index of the previous occurence of `element` in the `list` starting from the `from` index (exclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let list = List.fromArray(['c', 'o', 'f', 'f', 'e', 'e']); - /// assert List.prevIndexOf(list, Char.equal, 'c', List.size(list)) == ?0; - /// assert List.prevIndexOf(list, Char.equal, 'e', List.size(list)) == ?5; - /// assert List.prevIndexOf(list, Char.equal, 'e', 5) == ?4; - /// assert List.prevIndexOf(list, Char.equal, 'e', 4) == null; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func prevIndexOf(self : List, equal : (implicit : (T, T) -> Bool), element : T, fromExclusive : Nat) : ?Nat { - if (fromExclusive > size(self)) Prim.trap "List index out of bounds in prevIndexOf"; - - let blocks = self.blocks; - let (blockIndex, elementIndex) = locate(fromExclusive); - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) if (equal(x, element)) return ?indexByBlockElement(i, j); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - }; - - null - }; - - /// Returns the first value in `list` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let list = List.fromArray([1, 9, 4, 8]); - /// let found = List.find(list, func(x) { x > 8 }); - /// assert found == ?9; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func find(self : List, predicate : T -> Bool) : ?T { - Option.map(findIndex(self, predicate), func(i) = at(self, i)) - }; - - /// Finds the index of the first element in `list` for which `predicate` is true. - /// Returns `null` if no such element is found. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.findIndex(list, func(i) { i % 2 == 0 }) == ?1; - /// assert List.findIndex(list, func(i) { i > 5 }) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func findIndex(self : List, predicate : T -> Bool) : ?Nat { - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return null; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (predicate(x)) return ?indexByBlockElement(i, j); - case null return null - }; - j += 1 - }; - i += 1 - }; - null - }; - - /// Finds the index of the last element in `list` for which `predicate` is true. - /// Returns `null` if no such element is found. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.findLastIndex(list, func(i) { i % 2 == 0 }) == ?3; - /// assert List.findLastIndex(list, func(i) { i > 5 }) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func findLastIndex(self : List, predicate : T -> Bool) : ?Nat { - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) if (predicate(x)) return ?indexByBlockElement(i, j); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - }; - - null - }; - - /// Performs binary search on a sorted list to find the index of the `element`. - /// Returns `#found(index)` if the element is found, or `#insertionIndex(index)` with the index - /// where the element would be inserted according to the ordering if not found. - /// - /// If there are multiple equal elements, no guarantee is made about which index is returned. - /// The list must be sorted in ascending order according to the `compare` function. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 3, 5, 7, 9, 11]); - /// assert List.binarySearch(list, Nat.compare, 5) == #found(2); - /// assert List.binarySearch(list, Nat.compare, 6) == #insertionIndex(3); - /// ``` - /// - /// Runtime: `O(log(size))` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `compare` runs in `O(1)` time and space. - public func binarySearch(self : List, compare : (implicit : (T, T) -> Types.Order), element : T) : { - #found : Nat; - #insertionIndex : Nat - } { - // We call all data blocks of the same capacity an "epoch". We number the epochs 0,1,2,... - // A data block is in epoch e iff the data block has capacity 2 ** e. - // Each epoch starting with epoch 1 spans exactly two super blocks. - // Super block s falls in epoch ceil(s/2). - // Each epoch except e=0 contains 3 * 2 ** (e - 1) data blocks - - let blocks = self.blocks; - let b = self.blockIndex - (if (self.elementIndex == 0) 1 else 0) : Nat; - - // block index x such that blocks[x][0] <= element - let lessOrEqual = do { - // epoch of the last data block - let epoch = 32 - Nat32.bitcountLeadingZero(Nat32.fromNat(b) / 3); - // initially block index is the first in the epoch - var lessOrEqual = Nat32.toNat((1 << epoch) / 2); - - // lessOrEqual * 3 is always the first data block in an epoch - // while the first element of the first data block in an epoch is actually grater then element go to the previous epoch - // as the last epoch is half of the array we each iteration of the search divides the interval in four - while (lessOrEqual != 0 and compare(Option.unwrap(blocks[lessOrEqual * 3][0]), element) == #greater) { - lessOrEqual /= 2 - }; - - lessOrEqual * 3 - }; - - // Linear search in e=0, there are just two elements - if (lessOrEqual == 0) { - let to = Nat.min(size(self), 2); - for (i in Nat.range(0, to)) { - let x = at(self, i); - switch (compare(x, element)) { - case (#less) {}; - case (#equal) return #found(i); - case (#greater) return #insertionIndex(i) - } - }; - return #insertionIndex(to) - }; - - // binary search the blockIndex in [left, right) - let blockIndex = do { - // guarateed less or equal to element - var left = lessOrEqual; - // right is either outside of the array or greater than element - var right = Nat.min(b + 1, lessOrEqual * 2); - while (right - left : Nat > 1) { - let mid = (left + right) / 2; - switch (compare(Option.unwrap(blocks[mid][0]), element)) { - case (#less) left := mid; - case (#greater) right := mid; - case (#equal) return #found(indexByBlockElement(mid, 0)) - } - }; - left - }; - - // binary search the elementIndex - let elementIndex = do { - let block = blocks[blockIndex]; - var left = 0; - var right = if (blockIndex == self.blockIndex) self.elementIndex else block.size(); - while (left != right) { - let mid = (left + right) / 2; - switch (compare(Option.unwrap(block[mid]), element)) { - case (#less) left := mid + 1; - case (#greater) right := mid; - case (#equal) return #found(indexByBlockElement(blockIndex, mid)) - } - }; - left - }; - - #insertionIndex(indexByBlockElement(blockIndex, elementIndex)) - }; - - /// Returns true iff every element in `list` satisfies `predicate`. - /// In particular, if `list` is empty the function returns `true`. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.all(list, func x { x > 1 }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func all(self : List, predicate : T -> Bool) : Bool { - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return true; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) if (not predicate(x)) return false; - case null return true - }; - j += 1 - }; - i += 1 - }; - true - }; - - /// Returns true iff some element in `list` satisfies `predicate`. - /// In particular, if `list` is empty the function returns `false`. - /// - /// Example: - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 2); - /// List.add(list, 3); - /// List.add(list, 4); - /// - /// assert List.any(list, func x { x > 3 }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func any(self : List, predicate : T -> Bool) : Bool = findIndex(self, predicate) != null; - - /// Returns an Iterator (`Iter`) over the elements of a List. - /// Iterator provides a single method `next()`, which returns - /// elements in order, or `null` when out of elements to iterate over. - /// - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// - /// var sum = 0; - /// for (element in List.values(list)) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - public func values(self : List) : Types.Iter = object { - let blocks = self.blocks.size(); - var blockIndex = 0; - var elementIndex = 0; - var db : [var ?T] = self.blocks[blockIndex]; - var dbSize = db.size(); - - public func next() : ?T { - if (elementIndex == dbSize) { - blockIndex += 1; - if (blockIndex >= blocks) return null; - db := self.blocks[blockIndex]; - dbSize := db.size(); - if (dbSize == 0) return null; - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?x) { - elementIndex += 1; - return ?x - }; - case (_) return null - } - } - }; - - /// Returns an Iterator (`Iter`) over the items (index-value pairs) in the list. - /// Each item is a tuple of `(index, value)`. The iterator provides a single method - /// `next()` which returns elements in order, or `null` when out of elements. - /// - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// assert Iter.toArray(List.enumerate(list)) == [(0, 10), (1, 11), (2, 12)]; - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - /// - /// Warning: Allocates memory on the heap to store ?(Nat, T). - public func enumerate(self : List) : Types.Iter<(Nat, T)> = object { - let blocks = self.blocks.size(); - var blockIndex = 0; - var elementIndex = 0; - var size = 0; - var db : [var ?T] = [var]; - var i = 0; - - public func next() : ?(Nat, T) { - if (elementIndex == size) { - blockIndex += 1; - if (blockIndex >= blocks) return null; - db := self.blocks[blockIndex]; - size := db.size(); - if (size == 0) return null; - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?x) { - let ret = ?(i, x); - elementIndex += 1; - i += 1; - return ret - }; - case (_) return null - } - } - }; - - /// Returns an Iterator (`Iter`) over the elements of the list in reverse order. - /// The iterator provides a single method `next()` which returns elements from - /// last to first, or `null` when out of elements. - /// - /// ```motoko include=import - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// - /// var sum = 0; - /// for (element in List.reverseValues(list)) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - public func reverseValues(self : List) : Types.Iter = object { - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex; - var db : [var ?T] = if (blockIndex < self.blocks.size()) { - self.blocks[blockIndex] - } else { [var] }; - - public func next() : ?T { - if (elementIndex != 0) { - elementIndex -= 1 - } else { - blockIndex -= 1; - if (blockIndex == 0) return null; - db := self.blocks[blockIndex]; - elementIndex := db.size() - 1 - }; - - db[elementIndex] - } - }; - - /// Returns an Iterator (`Iter`) over the items in reverse order, i.e. pairs of index and value. - /// Iterator provides a single method `next()`, which returns - /// elements in reverse order, or `null` when out of elements to iterate over. - /// - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let list = List.empty(); - /// List.add(list, 10); - /// List.add(list, 11); - /// List.add(list, 12); - /// assert Iter.toArray(List.reverseEnumerate(list)) == [(2, 12), (1, 11), (0, 10)]; - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - /// - /// Warning: Allocates memory on the heap to store ?(T, Nat). - public func reverseEnumerate(self : List) : Types.Iter<(Nat, T)> = object { - var i = size(self); - var blockIndex = self.blockIndex; - var elementIndex = self.elementIndex; - var db : [var ?T] = if (blockIndex < self.blocks.size()) { - self.blocks[blockIndex] - } else { [var] }; - - public func next() : ?(Nat, T) { - if (elementIndex != 0) { - elementIndex -= 1 - } else { - blockIndex -= 1; - if (blockIndex == 0) return null; - db := self.blocks[blockIndex]; - elementIndex := db.size() - 1 - }; - switch (db[elementIndex]) { - case (?x) { - i -= 1; - return ?(i, x) - }; - case (_) Prim.trap INTERNAL_ERROR - } - } - }; - - /// Returns an Iterator (`Iter`) over the indices (keys) of the list. - /// The iterator provides a single method `next()` which returns indices - /// from 0 to size-1, or `null` when out of elements. - /// - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let list = List.empty(); - /// List.add(list, "A"); - /// List.add(list, "B"); - /// List.add(list, "C"); - /// Iter.toArray(List.keys(list)) // [0, 1, 2] - /// ``` - /// - /// Note: This does not create a snapshot. If the returned iterator is not consumed at once, - /// and instead the consumption of the iterator is interleaved with other operations on the - /// List, then this may lead to unexpected results. - /// - /// Runtime: `O(1)` - public func keys(self : List) : Types.Iter = Nat.range(0, size(self)); - - /// Creates a new List containing all elements from the provided iterator. - /// Elements are added in the order they are returned by the iterator. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [1, 1, 1]; - /// let iter = array.vals(); - /// - /// let list = List.fromIter(iter); - /// assert Iter.toArray(List.values(list)) == [1, 1, 1]; - /// ``` - /// - /// Runtime: `O(size)` - public func fromIter(iter : Types.Iter) : List { - let list = empty(); - for (element in iter) add(list, element); - list - }; - - /// Convert an iterator to a new mutable List. - /// Elements are added in the order they are returned by the iterator. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [1, 1, 1]; - /// let iter = array.vals(); - /// - /// let list = iter.toList(); - /// assert Iter.toArray(List.values(list)) == [1, 1, 1]; - /// ``` - /// - /// Runtime: `O(size)` - public func toList(self : Types.Iter) : List { - fromIter(self) - }; - - /// Appends all elements from `added` to the end of `list`. - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2]); - /// let added = List.fromArray([3, 4]); - /// List.append(list, added); - /// assert List.toArray(list) == [1, 2, 3, 4]; - /// ``` - /// - /// Runtime: `O(size(added))` - /// - /// Space: `O(size(added))` - public func append(self : List, added : List) { - reserve(self, size(added)); - - let blocks = added.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) addUnsafe(self, x); - case null return - }; - j += 1 - }; - i += 1 - } - }; - - /// Adds all elements from the provided iterator to the end of the list. - /// Elements are added in the order they are returned by the iterator. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [1, 1, 1]; - /// let iter = array.vals(); - /// let list = List.repeat(2, 1); - /// - /// List.addAll(list, iter); - /// assert Iter.toArray(List.values(list)) == [2, 1, 1, 1]; - /// ``` - /// - /// The maximum number of elements in a `List` is 2^32. - /// - /// Runtime: `O(size)`, where n is the size of iter. - public func addAll(self : List, iter : Types.Iter) { - for (element in iter) add(self, element) - }; - - /// Creates a new immutable array containing all elements from the list. - /// Elements appear in the same order as in the list. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3]); - /// - /// assert List.toArray(list) == [1, 2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - public func toArray(self : List) : [T] { - var blockIndex = 0; - var elementIndex = 0; - var sz = 0; - var db : [var ?T] = [var]; - - func generator(_ : Nat) : T { - if (elementIndex == sz) { - blockIndex += 1; - db := self.blocks[blockIndex]; - sz := db.size(); - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?x) { - elementIndex += 1; - return x - }; - case (_) Prim.trap INTERNAL_ERROR - } - }; - - Array.tabulate(size(self), generator) - }; - - /// Creates a List containing elements from an Array. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [2, 3]; - /// let list = List.fromArray(array); - /// assert Iter.toArray(List.values(list)) == [2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - public func fromArray(array : [T]) : List { - let (blockIndex, elementIndex) = locate(array.size()); - - let blocks = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) { blockIndex - 1 } else blockIndex)); - let dataBlocks = VarArray.repeat<[var ?T]>([var], blocks); - - var i = 1; - var pos = 0; - - while (i < blockIndex) { - let len = dataBlockSize(i); - dataBlocks[i] := VarArray.tabulate(len, func i = ?array[pos + i]); - pos += len; - i += 1 - }; - if (elementIndex != 0 and blockIndex < blocks) { - dataBlocks[i] := VarArray.tabulate( - dataBlockSize(i), - func i = if (i < elementIndex) ?array[pos + i] else null - ) - }; - - { - var blocks = dataBlocks; - var blockIndex = blockIndex; - var elementIndex = elementIndex - } - }; - - /// Creates a new mutable array containing all elements from the list. - /// Elements appear in the same order as in the list. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// let varArray = List.toVarArray(list); - /// assert Array.fromVarArray(varArray) == [1, 2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - public func toVarArray(self : List) : [var T] { - let ?fs = first(self) else return [var]; - - let array = VarArray.repeat(fs, size(self)); - - var index = 0; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return array; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) array[index] := x; - case null return array - }; - j += 1; - index += 1 - }; - i += 1 - }; - array - }; - - /// Creates a new List containing all elements from the mutable array. - /// Elements appear in the same order as in the array. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let array = [var 2, 3]; - /// let list = List.fromVarArray(array); - /// assert Iter.toArray(List.values(list)) == [2, 3]; - /// ``` - /// - /// Runtime: `O(size)` - public func fromVarArray(array : [var T]) : List { - let (blockIndex, elementIndex) = locate(array.size()); - - let blocks = newIndexBlockLength(Nat32.fromNat(if (elementIndex == 0) { blockIndex - 1 } else blockIndex)); - let dataBlocks = VarArray.repeat<[var ?T]>([var], blocks); - - func makeBlock(array : [var T], p : Nat, len : Nat, fill : Nat) : [var ?T] { - let block = VarArray.repeat(null, len); - var j = 0; - var pos = p; - while (j < fill) { - block[j] := ?array[pos]; - j += 1; - pos += 1 - }; - block - }; - - var i = 1; - var pos = 0; - - while (i < blockIndex) { - let len = dataBlockSize(i); - dataBlocks[i] := makeBlock(array, pos, len, len); - pos += len; - i += 1 - }; - if (elementIndex != 0) { - dataBlocks[i] := makeBlock(array, pos, dataBlockSize(i), elementIndex) - }; - - { - var blocks = dataBlocks; - var blockIndex = blockIndex; - var elementIndex = elementIndex - } - }; - - /// Returns the first element of `list`, or `null` if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// assert List.first(List.fromArray([1, 2, 3])) == ?1; - /// assert List.first(List.empty()) == null; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func first(self : List) : ?T { - if (self.blockIndex == 1) null else self.blocks[1][0] - }; - - /// Returns the last element of `list`, or `null` if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// assert List.last(List.fromArray([1, 2, 3])) == ?3; - /// assert List.last(List.empty()) == null; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func last(self : List) : ?T { - let e = self.elementIndex; - if (e > 0) return self.blocks[self.blockIndex][e - 1]; - - let b = self.blockIndex - 1 : Nat; - if (b == 0) null else { - let block = self.blocks[b]; - block[block.size() - 1] - } - }; - - /// Applies `f` to each element in `list`. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// List.forEach(list, func(x) { - /// Debug.print(Nat.toText(x)); // prints each element in list - /// }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEach(self : List, f : T -> ()) { - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) f(x); - case null return - }; - j += 1 - }; - i += 1 - } - }; - - /// Applies `f` to each item `(i, x)` in `list` where `i` is the key - /// and `x` is the value. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// List.forEachEntry(list, func (i,x) { - /// // prints each item (i,x) in list - /// Debug.print(Nat.toText(i) # Nat.toText(x)); - /// }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEachEntry(self : List, f : (Nat, T) -> ()) { - var index = 0; - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) f(index, x); - case null return - }; - j += 1; - index += 1 - }; - i += 1 - } - }; - - func actualInterval(fromInclusive : Int, toExclusive : Int, size : Nat) : (Nat, Nat) { - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - (Prim.abs(startInt), Prim.abs(endInt)) - }; - - /// Returns an iterator over a slice of `list` starting at `fromInclusive` up to (but not including) `toExclusive`. - /// - /// Negative indices are relative to the end of the list. For example, `-1` corresponds to the last element in the list. - /// - /// If the indices are out of bounds, they are clamped to the list bounds. - /// If the first index is greater than the second, the function returns an empty iterator. - /// - /// ```motoko include=import - /// let list = List.fromArray([1, 2, 3, 4, 5]); - /// let iter1 = List.range(list, 3, List.size(list)); - /// assert iter1.next() == ?4; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// let iter2 = List.range(list, 3, -1); - /// assert iter2.next() == ?4; - /// assert iter2.next() == null; - /// - /// let iter3 = List.range(list, 0, 0); - /// assert iter3.next() == null; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func range(self : List, fromInclusive : Int, toExclusive : Int) : Types.Iter = object { - let (start, end) = actualInterval(fromInclusive, toExclusive, size(self)); - let blocks = self.blocks.size(); - var blockIndex = 0; - var elementIndex = 0; - if (start != 0) { - let (block, element) = locate(start - 1); - blockIndex := block; - elementIndex := element + 1 - }; - var db : [var ?T] = self.blocks[blockIndex]; - var dbSize = db.size(); - var index = fromInclusive; - - public func next() : ?T { - if (index >= end) return null; - index += 1; - - if (elementIndex == dbSize) { - blockIndex += 1; - if (blockIndex >= blocks) return null; - db := self.blocks[blockIndex]; - dbSize := db.size(); - if (dbSize == 0) return null; - elementIndex := 0 - }; - let ret = db[elementIndex]; - elementIndex += 1; - ret - } - }; - - func sliceToArrayBase(self : List, start : Nat) : { - next(i : Nat) : T - } = object { - var blockIndex = 0; - var elementIndex = 0; - if (start != 0) { - let (block, element) = locate(start - 1); - blockIndex := block; - elementIndex := element + 1 - }; - var db : [var ?T] = self.blocks[blockIndex]; - var dbSize = db.size(); - - public func next(i : Nat) : T { - if (elementIndex == dbSize) { - blockIndex += 1; - db := self.blocks[blockIndex]; - dbSize := db.size(); - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?x) { - elementIndex += 1; - return x - }; - case null Prim.trap INTERNAL_ERROR - } - } - }; - - /// Returns a new array containing elements from `list` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// let array = List.fromArray([1, 2, 3, 4, 5]); - /// - /// let slice1 = List.sliceToArray(array, 1, 4); - /// assert slice1 == [2, 3, 4]; - /// - /// let slice2 = List.sliceToArray(array, 1, -1); - /// assert slice2 == [2, 3, 4]; - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToArray(self : List, fromInclusive : Int, toExclusive : Int) : [T] { - let (start, end) = actualInterval(fromInclusive, toExclusive, size(self)); - Array.tabulate(end - start, sliceToArrayBase(self, start).next) - }; - - /// Returns a new var array containing elements from `list` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// import VarArray "mo:core/VarArray"; - /// import Nat "mo:core/Nat"; - /// - /// let array = List.fromArray([1, 2, 3, 4, 5]); - /// - /// let slice1 = List.sliceToVarArray(array, 1, 4); - /// assert VarArray.equal(slice1, [var 2, 3, 4], Nat.equal); - /// - /// let slice2 = List.sliceToVarArray(array, 1, -1); - /// assert VarArray.equal(slice2, [var 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToVarArray(self : List, fromInclusive : Int, toExclusive : Int) : [var T] { - let (start, end) = actualInterval(fromInclusive, toExclusive, size(self)); - VarArray.tabulate(end - start, sliceToArrayBase(self, start).next) - }; - - /// Like `forEachEntryRev` but iterates through the list in reverse order, - /// from end to beginning. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// List.reverseForEachEntry(list, func (i,x) { - /// // prints each item (i,x) in list - /// Debug.print(Nat.toText(i) # Nat.toText(x)); - /// }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func reverseForEachEntry(self : List, f : (Nat, T) -> ()) { - var index = 0; - - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) f(index, x); - case null Prim.trap INTERNAL_ERROR - }; - index += 1 - }; - i -= 1 - } - }; - - /// Applies `f` to each element in `list` in reverse order. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// let list = List.fromArray([1, 2, 3]); - /// - /// List.reverseForEach(list, func (x) { - /// Debug.print(Nat.toText(x)); // prints each element in list in reverse order - /// }); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func reverseForEach(self : List, f : T -> ()) { - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) f(x); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - } - }; - - /// Executes the closure over a slice of `list` starting at `fromInclusive` up to (but not including) `toExclusive`. - /// - /// ```motoko include=import - /// import Debug "mo:core/Debug"; - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1, 2, 3, 4, 5]); - /// List.forEachInRange(list, func x = Debug.print(Nat.toText(x)), 1, 2); // prints 2 and 3 - /// ``` - /// - /// Runtime: `O(toExclusive - fromExclusive)` - /// - /// Space: `O(1)` - public func forEachInRange(self : List, f : T -> (), fromInclusive : Nat, toExclusive : Nat) { - if (not (fromInclusive <= toExclusive and toExclusive <= size(self))) Prim.trap("Invalid range"); - - func traverseBlock(block : [var ?T], f : T -> (), from : Nat, to : Nat) { - var i = from; - while (i < to) { - switch (block[i]) { - case (?value) f(value); - case null Prim.trap(INTERNAL_ERROR) - }; - i += 1 - } - }; - - let (fromBlock, fromElement) = locate(fromInclusive); - let (toBlock, toElement) = locate(toExclusive); - - let blocks = self.blocks; - let sz = blocks.size(); - - if (fromBlock == toBlock) { - if (fromBlock < sz) traverseBlock(blocks[fromBlock], f, fromElement, toElement); - return - }; - - traverseBlock(blocks[fromBlock], f, fromElement, blocks[fromBlock].size()); - - var i = fromBlock + 1; - let to = Nat.min(toBlock, sz); - while (i < to) { - traverseBlock(blocks[i], f, 0, blocks[i].size()); - i += 1 - }; - - if (toBlock < sz) traverseBlock(blocks[toBlock], f, 0, toElement) - }; - - /// Returns true if the list contains the specified element according to the provided - /// equality function. Uses the provided `equal` function to compare elements. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 2); - /// List.add(list, 0); - /// List.add(list, 3); - /// - /// assert List.contains(list, Nat.equal, 2); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func contains(self : List, equal : (implicit : (T, T) -> Bool), element : T) : Bool { - Option.isSome(indexOf(self, equal, element)) - }; - - /// Returns the greatest element in the list according to the ordering defined by `compare`. - /// Returns `null` if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// - /// assert List.max(list, Nat.compare) == ?2; - /// assert List.max(List.empty(), Nat.compare) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func max(self : List, compare : (implicit : (T, T) -> Types.Order)) : ?T { - var maxSoFar : T = switch (first(self)) { - case (?x) x; - case null return null - }; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return ?maxSoFar; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) switch (compare(x, maxSoFar)) { - case (#greater) maxSoFar := x; - case _ {} - }; - case null return ?maxSoFar - }; - j += 1 - }; - i += 1 - }; - - ?maxSoFar - }; - - /// Returns the least element in the list according to the ordering defined by `compare`. - /// Returns `null` if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.empty(); - /// List.add(list, 1); - /// List.add(list, 2); - /// - /// assert List.min(list, Nat.compare) == ?1; - /// assert List.min(List.empty(), Nat.compare) == null; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func min(self : List, compare : (implicit : (T, T) -> Types.Order)) : ?T { - var minSoFar : T = switch (first(self)) { - case (?x) x; - case null return null - }; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return ?minSoFar; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) switch (compare(x, minSoFar)) { - case (#less) minSoFar := x; - case _ {} - }; - case null return ?minSoFar - }; - j += 1 - }; - i += 1 - }; - - ?minSoFar - }; - - /// Tests if two lists are equal by comparing their elements using the provided `equal` function. - /// Returns true if and only if both lists have the same size and all corresponding elements - /// are equal according to the provided function. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list1 = List.fromArray([1,2]); - /// let list2 = List.empty(); - /// List.add(list2, 1); - /// List.add(list2, 2); - /// - /// assert List.equal(list1, list2, Nat.equal); - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func equal(self : List, other : List, equal : (implicit : (T, T) -> Bool)) : Bool { - if (size(self) != size(other)) return false; - - let blocks1 = self.blocks; - let blocks2 = other.blocks; - let blockCount = Nat.min(blocks1.size(), blocks2.size()); - - var i = 1; - while (i < blockCount) { - let db1 = blocks1[i]; - let db2 = blocks2[i]; - let sz = Nat.min(db1.size(), db2.size()); - if (sz == 0) return true; - - var j = 0; - while (j < sz) { - switch (db1[j], db2[j]) { - case (?x, ?y) if (not equal(x, y)) return false; - case (_, _) return true - }; - j += 1 - }; - i += 1 - }; - return true - }; - - /// Compares two lists lexicographically using the provided `compare` function. - /// Elements are compared pairwise until a difference is found or one list ends. - /// If all elements compare equal, the shorter list is considered less than the longer list. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list1 = List.fromArray([0, 1]); - /// let list2 = List.fromArray([2]); - /// let list3 = List.fromArray([0, 1, 2]); - /// - /// assert List.compare(list1, list2, Nat.compare) == #less; - /// assert List.compare(list1, list3, Nat.compare) == #less; - /// assert List.compare(list2, list3, Nat.compare) == #greater; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func compare(self : List, other : List, compare : (implicit : (T, T) -> Types.Order)) : Types.Order { - let blocks1 = self.blocks; - let blocks2 = other.blocks; - let blockCount = Nat.min(blocks1.size(), blocks2.size()); - - var i = 1; - label l while (i < blockCount) { - let db1 = blocks1[i]; - let db2 = blocks2[i]; - let sz = Nat.min(db1.size(), db2.size()); - if (sz == 0) break l; - - var j = 0; - while (j < sz) { - switch (db1[j], db2[j]) { - case (?x, ?y) switch (compare(x, y)) { - case (#less) return #less; - case (#greater) return #greater; - case _ {} - }; - case (_, _) break l - }; - j += 1 - }; - i += 1 - }; - return Nat.compare(size(self), size(other)) - }; - - /// Creates a textual representation of `list`, using `toText` to recursively - /// convert the elements into Text. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1,2,3,4]); - /// - /// assert List.toText(list, Nat.toText) == "List[1, 2, 3, 4]"; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `toText` runs in O(1) time and space. - public func toText(self : List, toText : (implicit : T -> Text)) : Text { - var text = switch (first(self)) { - case (?x) toText(x); - case null "" - }; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 2; - label l while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) break l; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) text #= ", " # toText(x); - case null break l - }; - j += 1 - }; - i += 1 - }; - - "List[" # text # "]" - }; - - /// Collapses the elements in `list` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1,2,3]); - /// - /// assert List.foldLeft(list, "", func (acc, x) { acc # Nat.toText(x)}) == "123"; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `combine` runs in O(1)` time and space. - public func foldLeft(self : List, base : A, combine : (A, T) -> A) : A { - var accumulation = base; - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return accumulation; - - var j = 0; - while (j < sz) { - switch (db[j]) { - case (?x) accumulation := combine(accumulation, x); - case null return accumulation - }; - j += 1 - }; - i += 1 - }; - accumulation - }; - - /// Collapses the elements in `list` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let list = List.fromArray([1,2,3]); - /// - /// assert List.foldRight(list, "", func (x, acc) { Nat.toText(x) # acc }) == "123"; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - /// - /// *Runtime and space assumes that `combine` runs in O(1)` time and space. - public func foldRight(self : List, base : A, combine : (T, A) -> A) : A { - var accumulation = base; - - let blocks = self.blocks; - let blockIndex = self.blockIndex; - let elementIndex = self.elementIndex; - - var i = blockIndex; - if (elementIndex == 0) i -= 1; - - while (i > 0) { - let db = blocks[i]; - let sz = db.size(); - var j = if (i == blockIndex) elementIndex else sz; - while (j > 0) { - j -= 1; - switch (db[j]) { - case (?x) accumulation := combine(x, accumulation); - case null Prim.trap INTERNAL_ERROR - } - }; - i -= 1 - }; - - accumulation - }; - - /// Reverses the order of elements in `list` by overwriting in place. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let list = List.fromArray([1,2,3]); - /// - /// List.reverseInPlace(list); - /// assert Iter.toArray(List.values(list)) == [3, 2, 1]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func reverseInPlace(self : List) { - let vsize = size(self); - if (vsize <= 1) return; - - let (finalBlock, finalElement) = locate(vsize / 2); - - let blocks = self.blocks; - - var blockIndexBack = self.blockIndex; - var elementIndexBack = self.elementIndex; - var dbBack : [var ?T] = if (blockIndexBack < self.blocks.size()) { - self.blocks[blockIndexBack] - } else { [var] }; - - var i = 1; - var index = 0; - while (i <= finalBlock) { - let db = blocks[i]; - let sz = if (i == finalBlock) finalElement else db.size(); - - var j = 0; - while (j < sz) { - if (elementIndexBack == 0) { - blockIndexBack -= 1; - dbBack := self.blocks[blockIndexBack]; - elementIndexBack := dbBack.size() - 1 - } else { - elementIndexBack -= 1 - }; - - let temp = db[j]; - db[j] := dbBack[elementIndexBack]; - dbBack[elementIndexBack] := temp; - - j += 1; - index += 1 - }; - i += 1 - } - }; - - /// Returns a new List with the elements from `list` in reverse order. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let list = List.fromArray([1,2,3]); - /// - /// let rlist = List.reverse(list); - /// assert Iter.toArray(List.values(rlist)) == [3, 2, 1]; - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func reverse(self : List) : List { - let rlist = repeatInternal(null, size(self)); - - let blocks = self.blocks; - let blockCount = blocks.size(); - - var blockIndexBack = rlist.blockIndex; - var elementIndexBack = rlist.elementIndex; - var dbBack : [var ?T] = if (blockIndexBack < rlist.blocks.size()) { - rlist.blocks[blockIndexBack] - } else { [var] }; - - var i = 1; - while (i < blockCount) { - let db = blocks[i]; - let sz = db.size(); - if (sz == 0) return rlist; - - var j = 0; - while (j < sz) { - if (elementIndexBack == 0) { - blockIndexBack -= 1; - if (blockIndexBack == 0) return rlist; - dbBack := rlist.blocks[blockIndexBack]; - elementIndexBack := dbBack.size() - 1 - } else { - elementIndexBack -= 1 - }; - - dbBack[elementIndexBack] := db[j]; - j += 1 - }; - i += 1 - }; - rlist - }; - - /// Returns true if and only if the list is empty. - /// - /// Example: - /// ```motoko include=import - /// let list = List.fromArray([2,0,3]); - /// assert not List.isEmpty(list); - /// assert List.isEmpty(List.empty()); - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func isEmpty(self : List) : Bool { - self.blockIndex == 1 - }; - - /// Unsafe iterator starting from `start`. - /// - /// Example: - /// ``` - /// let list = List.fromArray([1, 2, 3, 4, 5]); - /// let reader = List.reader(list, 2); - /// assert reader() == 3; - /// assert reader() == 4; - /// assert reader() == 5; - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func reader(self : List, start : Nat) : () -> T { - var blockIndex = 0; - var elementIndex = 0; - if (start != 0) { - let (block, element) = locate(start - 1); - blockIndex := block; - elementIndex := element + 1 - }; - var db : [var ?T] = self.blocks[blockIndex]; - var dbSize = db.size(); - func next() : T { - // Note: next() traps when reading beyond end of list - if (elementIndex == dbSize) { - blockIndex += 1; - db := self.blocks[blockIndex]; - dbSize := db.size(); - elementIndex := 0 - }; - switch (db[elementIndex]) { - case (?ret) { - elementIndex += 1; - return ret - }; - case (_) Prim.trap("List.reader(): out of bounds") - } - }; - next - }; - -} diff --git a/.mops/core@2.5.0/src/Map.mo b/.mops/core@2.5.0/src/Map.mo deleted file mode 100644 index 6e10175..0000000 --- a/.mops/core@2.5.0/src/Map.mo +++ /dev/null @@ -1,2672 +0,0 @@ -/// An imperative key-value map based on order/comparison of the keys. -/// The map data structure type is stable and can be used for orthogonal persistence. -/// -/// Example: -/// ```motoko -/// import Map "mo:core/Map"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// // creation -/// let map = Map.empty(); -/// // insertion -/// Map.add(map, Nat.compare, 0, "Zero"); -/// // retrieval -/// assert Map.get(map, Nat.compare, 0) == ?"Zero"; -/// assert Map.get(map, Nat.compare, 1) == null; -/// // removal -/// Map.remove(map, Nat.compare, 0); -/// assert Map.isEmpty(map); -/// } -/// ``` -/// -/// The internal implementation is a B-tree with order 32. -/// -/// Performance: -/// * Runtime: `O(log(n))` worst case cost per insertion, removal, and retrieval operation. -/// * Space: `O(n)` for storing the entire map. -/// `n` denotes the number of key-value entries stored in the map. - -// Data structure implementation is courtesy of Byron Becker. -// Source: https://github.com/canscale/StableHeapBTreeMap -// Copyright (c) 2022 Byron Becker. -// Distributed under Apache 2.0 license. -// With adjustments by the Motoko team. - -import PureMap "pure/Map"; -import Types "Types"; -import Iter "Iter"; -import Order "Order"; -import VarArray "VarArray"; -import Runtime "Runtime"; -import Stack "Stack"; -import Option "Option"; -import BTreeHelper "internal/BTreeHelper"; - -module { - let btreeOrder = 32; // Should be >= 4 and <= 512. - - public type Map = Types.Map; - - type Node = Types.Map.Node; - type Data = Types.Map.Data; - type Internal = Types.Map.Internal; - type Leaf = Types.Map.Leaf; - - /// Convert the mutable key-value map to an immutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import PureMap "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), Nat.compare); - /// let pureMap = Map.toPure(map, Nat.compare); - /// assert Iter.toArray(PureMap.entries(pureMap)) == Iter.toArray(Map.entries(map)) - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - /// @deprecated M0235 - public func toPure(self : Map, compare : (implicit : (K, K) -> Order.Order)) : PureMap.Map { - PureMap.fromIter(entries(self), compare) - }; - - /// Convert an immutable key-value map to a mutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import PureMap "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let pureMap = PureMap.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), Nat.compare); - /// let map = Map.fromPure(pureMap, Nat.compare); - /// assert Iter.toArray(Map.entries(map)) == Iter.toArray(PureMap.entries(pureMap)) - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func fromPure(map : PureMap.Map, compare : (implicit : (K, K) -> Order.Order)) : Map { - fromIter(PureMap.entries(map), compare) - }; - - /// Create a copy of the mutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let originalMap = Map.fromIter( - /// [(1, "One"), (2, "Two"), (3, "Three")].values(), Nat.compare); - /// let clonedMap = Map.clone(originalMap); - /// Map.add(originalMap, Nat.compare, 4, "Four"); - /// assert Map.size(clonedMap) == 3; - /// assert Map.size(originalMap) == 4; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func clone(self : Map) : Map { - { - var root = cloneNode(self.root); - var size = self.size - } - }; - - /// Create a new empty mutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// assert Map.size(map) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Map { - { - var root = #leaf({ - data = { - kvs = VarArray.repeat(null, btreeOrder - 1); - var count = 0 - } - }); - var size = 0 - } - }; - - /// Create a new mutable key-value map with a single entry. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.singleton(0, "Zero"); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero")]; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func singleton(key : K, value : V) : Map { - let kvs = VarArray.repeat(null, btreeOrder - 1); - kvs[0] := ?(key, value); - { - var root = #leaf { data = { kvs; var count = 1 } }; - var size = 1 - } - }; - - /// Delete all the entries in the key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert Map.size(map) == 3; - /// - /// Map.clear(map); - /// assert Map.size(map) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func clear(self : Map) { - let emptyMap = empty(); - self.root := emptyMap.root; - self.size := 0 - }; - - /// Determines whether a key-value map is empty. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert not Map.isEmpty(map); - /// Map.clear(map); - /// assert Map.isEmpty(map); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func isEmpty(self : Map) : Bool { - self.size == 0 - }; - - /// Return the number of entries in a key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert Map.size(map) == 3; - /// Map.clear(map); - /// assert Map.size(map) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func size(self : Map) : Nat { - self.size - }; - - /// Test whether two imperative maps have equal entries. - /// Both maps have to be constructed by the same comparison function. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let map1 = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// let map2 = Map.clone(map1); - /// - /// assert Map.equal(map1, map2, Nat.compare, Text.equal); - /// Map.clear(map2); - /// assert not Map.equal(map1, map2, Nat.compare, Text.equal); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func equal(self : Map, other : Map, compare : (implicit : (K, K) -> Types.Order), equal : (implicit : (V, V) -> Bool)) : Bool { - if (size(self) != size(other)) { - return false - }; - let iterator1 = entries(self); - let iterator2 = entries(other); - loop { - let next1 = iterator1.next(); - let next2 = iterator2.next(); - switch (next1, next2) { - case (null, null) { - return true - }; - case (?(key1, value1), ?(key2, value2)) { - if ( - not (compare(key1, key2) == #equal) or - not equal(value1, value2) - ) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Tests whether the map contains the provided key. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert Map.containsKey(map, Nat.compare, 1); - /// assert not Map.containsKey(map, Nat.compare, 3); - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func containsKey(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : Bool { - Option.isSome(get(self, compare, key)) - }; - - /// Get the value associated with key in the given map if present and `null` otherwise. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (1, "One"), (2, "Two")].values(), - /// Nat.compare); - /// - /// assert Map.get(map, Nat.compare, 1) == ?"One"; - /// assert Map.get(map, Nat.compare, 3) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func get(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : ?V { - switch (self.root) { - case (#internal(internalNode)) { - getFromInternal(internalNode, compare, key) - }; - case (#leaf(leafNode)) { getFromLeaf(leafNode, compare, key) } - } - }; - - /// Given `map` ordered by `compare`, insert a new mapping from `key` to `value`. - /// Replaces any existing entry under `key`. - /// Returns true if the key is new to the map, otherwise false. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// assert Map.insert(map, Nat.compare, 0, "Zero"); - /// assert Map.insert(map, Nat.compare, 1, "One"); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One")]; - /// assert not Map.insert(map, Nat.compare, 0, "Nil"); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Nil"), (1, "One")] - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func insert(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : Bool { - switch (swap(self, compare, key, value)) { - case null true; - case _ false - } - }; - - /// Given `map` ordered by `compare`, add a mapping from `key` to `value` to `map`. - /// Replaces any existing entry for `key`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// - /// Map.add(map, Nat.compare, 0, "Zero"); - /// Map.add(map, Nat.compare, 1, "One"); - /// Map.add(map, Nat.compare, 0, "Nil"); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Nil"), (1, "One")] - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func add(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) { - ignore swap(self, compare, key, value) - }; - - /// Associates the value with the key in the map. - /// If the key is not yet present in the map, a new key-value pair is added and `null` is returned. - /// Otherwise, if the key is already present, the value is overwritten and the previous value is returned. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.singleton(1, "One"); - /// - /// assert Map.swap(map, Nat.compare, 0, "Zero") == null; - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One")]; - /// - /// assert Map.swap(map, Nat.compare, 0, "Nil") == ?"Zero"; - /// assert Iter.toArray(Map.entries(map)) == [(0, "Nil"), (1, "One")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func swap(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : ?V { - let insertResult = switch (self.root) { - case (#leaf(leafNode)) { - leafInsertHelper(leafNode, btreeOrder, compare, key, value) - }; - case (#internal(internalNode)) { - internalInsertHelper(internalNode, btreeOrder, compare, key, value) - } - }; - - switch (insertResult) { - case (#insert(ov)) { - switch (ov) { - // if inserted a value that was not previously there, increment the tree size counter - case null { self.size += 1 }; - case _ {} - }; - ov - }; - case (#promote({ kv; leftChild; rightChild })) { - let kvs = VarArray.repeat(null, btreeOrder - 1); - kvs[0] := ?kv; - let children = VarArray.repeat>(null, btreeOrder); - children[0] := ?leftChild; - children[1] := ?rightChild; - self.root := #internal({ - data = { - kvs; - var count = 1 - }; - children - }); - // promotion always comes from inserting a new element, so increment the tree size counter - self.size += 1; - - null - } - } - }; - - /// Overwrites the value of an existing key and returns the previous value. - /// If the key does not exist, it has no effect and returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.singleton(0, "Zero"); - /// - /// let prev1 = Map.replace(map, Nat.compare, 0, "Nil"); // overwrites the value for existing key. - /// assert prev1 == ?"Zero"; - /// assert Map.get(map, Nat.compare, 0) == ?"Nil"; - /// - /// let prev2 = Map.replace(map, Nat.compare, 1, "One"); // no effect, key is absent - /// assert prev2 == null; - /// assert Map.get(map, Nat.compare, 1) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func replace(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : ?V { - // TODO: Could be optimized in future - if (containsKey(self, compare, key)) { - swap(self, compare, key, value) - } else { - null - } - }; - - /// Delete an entry by its key in the map. - /// No effect if the key is not present. - /// - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (2, "Two"), (1, "One")].values(), - /// Nat.compare); - /// - /// Map.remove(map, Nat.compare, 1); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (2, "Two")]; - /// Map.remove(map, Nat.compare, 42); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - public func remove(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) { - ignore delete(self, compare, key) - }; - - /// Delete an existing entry by its key in the map. - /// Returns `true` if the key was present in the map, otherwise `false`. - /// - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (2, "Two"), (1, "One")].values(), - /// Nat.compare); - /// - /// assert Map.delete(map, Nat.compare, 1); // present, returns true - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (2, "Two")]; - /// - /// assert not Map.delete(map, Nat.compare, 42); // absent, returns false - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// @deprecated M0235 - public func delete(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : Bool { - switch (take(self, compare, key)) { - case null false; - case _ true - } - }; - - /// Removes any existing entry by its key in the map. - /// Returns the previous value of the key or `null` if the key was absent. - /// - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter( - /// [(0, "Zero"), (2, "Two"), (1, "One")].values(), - /// Nat.compare); - /// - /// assert Map.take(map, Nat.compare, 0) == ?"Zero"; - /// assert Iter.toArray(Map.entries(map)) == [(1, "One"), (2, "Two")]; - /// - /// assert Map.take(map, Nat.compare, 3) == null; - /// assert Iter.toArray(Map.entries(map)) == [(1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// @deprecated M0235 - public func take(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : ?V { - let deletedValue = switch (self.root) { - case (#leaf(leafNode)) { - // TODO: think about how this can be optimized so don't have to do two steps (search and then insert)? - switch (NodeUtil.getKeyIndex(leafNode.data, compare, key)) { - case (#keyFound(deleteIndex)) { - leafNode.data.count -= 1; - let (_, deletedValue) = BTreeHelper.deleteAndShift<(K, V)>(leafNode.data.kvs, deleteIndex); - self.size -= 1; - ?deletedValue - }; - case _ { null } - } - }; - case (#internal(internalNode)) { - let deletedValueResult = switch (internalDeleteHelper(internalNode, btreeOrder, compare, key, false)) { - case (#delete(value)) { value }; - case (#mergeChild({ internalChild; deletedValue })) { - if (internalChild.data.count > 0) { - self.root := #internal(internalChild) - } - // This case will be hit if the BTree has order == 4 - // In this case, the internalChild has no keys (last key was merged with new child), so need to promote that merged child (its only child) - else { - self.root := switch (internalChild.children[0]) { - case (?node) { node }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.delete(), element deletion failed, due to a null replacement node error") - } - } - }; - deletedValue - } - }; - switch (deletedValueResult) { - // if deleted a value from the BTree, decrement the size - case (?deletedValue) { self.size -= 1 }; - case null {} - }; - deletedValueResult - } - }; - deletedValue - }; - - public func toArray(self : Map) : [(K, V)] { - Iter.toArray(entries(self)) - }; - - public func toVarArray(self : Map) : [var (K, V)] { - Iter.toVarArray(entries(self)) - }; - - /// Retrieves the key-value pair from the map with the maximum key. - /// If the map is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// - /// assert Map.maxEntry(map) == null; - /// - /// Map.add(map, Nat.compare, 0, "Zero"); - /// Map.add(map, Nat.compare, 2, "Two"); - /// Map.add(map, Nat.compare, 1, "One"); - /// - /// assert Map.maxEntry(map) == ?(2, "Two") - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func maxEntry(self : Map) : ?(K, V) { - reverseEntries(self).next() - }; - - /// Retrieves the key-value pair from the map with the minimum key. - /// If the map is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// - /// assert Map.minEntry(map) == null; - /// - /// Map.add(map, Nat.compare, 2, "Two"); - /// Map.add(map, Nat.compare, 0, "Zero"); - /// Map.add(map, Nat.compare, 1, "One"); - /// - /// assert Map.minEntry(map) == ?(0, "Zero") - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func minEntry(self : Map) : ?(K, V) { - entries(self).next() - }; - - /// Returns an iterator over the key-value pairs in the map, - /// traversing the entries in the ascending order of the keys. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// var sum = 0; - /// var text = ""; - /// for ((k, v) in Map.entries(map)) { sum += k; text #= v }; - /// assert sum == 3; - /// assert text == "ZeroOneTwo" - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func entries(self : Map) : Types.Iter<(K, V)> { - switch (self.root) { - case (#leaf(leafNode)) { return leafEntries(leafNode) }; - case (#internal(internalNode)) { internalEntries(internalNode) } - } - }; - - /// Returns an iterator over the key-value pairs in the map, - /// starting from a given key in ascending order. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (3, "Three"), (1, "One")].values(), Nat.compare); - /// assert Iter.toArray(Map.entriesFrom(map, Nat.compare, 1)) == [(1, "One"), (3, "Three")]; - /// assert Iter.toArray(Map.entriesFrom(map, Nat.compare, 2)) == [(3, "Three")]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func entriesFrom( - self : Map, - compare : (implicit : (K, K) -> Order.Order), - key : K - ) : Types.Iter<(K, V)> { - switch (self.root) { - case (#leaf(leafNode)) leafEntriesFrom(leafNode, compare, key); - case (#internal(internalNode)) internalEntriesFrom(internalNode, compare, key) - } - }; - - /// Returns an iterator over the key-value pairs in the map, - /// traversing the entries in the descending order of the keys. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.reverseEntries(map)) == [(2, "Two"), (1, "One"), (0, "Zero")]; - /// var sum = 0; - /// var text = ""; - /// for ((k, v) in Map.reverseEntries(map)) { sum += k; text #= v }; - /// assert sum == 3; - /// assert text == "TwoOneZero" - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseEntries(self : Map) : Types.Iter<(K, V)> { - switch (self.root) { - case (#leaf(leafNode)) reverseLeafEntries(leafNode); - case (#internal(internalNode)) reverseInternalEntries(internalNode) - } - }; - - /// Returns an iterator over the key-value pairs in the map, - /// starting from a given key in descending order. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (1, "One"), (3, "Three")].values(), Nat.compare); - /// assert Iter.toArray(Map.reverseEntriesFrom(map, Nat.compare, 0)) == [(0, "Zero")]; - /// assert Iter.toArray(Map.reverseEntriesFrom(map, Nat.compare, 2)) == [(1, "One"), (0, "Zero")]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseEntriesFrom( - self : Map, - compare : (implicit : (K, K) -> Order.Order), - key : K - ) : Types.Iter<(K, V)> { - switch (self.root) { - case (#leaf(leafNode)) reverseLeafEntriesFrom(leafNode, compare, key); - case (#internal(internalNode)) reverseInternalEntriesFrom(internalNode, compare, key) - } - }; - - /// Returns an iterator over the keys in the map, - /// traversing all keys in ascending order. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.keys(map)) == [0, 1, 2]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func keys(self : Map) : Types.Iter { - object { - let iterator = entries(self); - - public func next() : ?K { - switch (iterator.next()) { - case null null; - case (?(key, _)) ?key - } - } - } - }; - - /// Returns an iterator over the values in the map, - /// traversing the values in the ascending order of the keys to which they are associated. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.values(map)) == ["Zero", "One", "Two"]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func values(self : Map) : Types.Iter { - object { - let iterator = entries(self); - - public func next() : ?V { - switch (iterator.next()) { - case null null; - case (?(_, value)) ?value - } - } - } - }; - - /// Create a mutable key-value map with the entries obtained from an iterator. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = - /// Iter.fromArray([(0, "Zero"), (2, "Two"), (1, "One")]); - /// - /// let map = Map.fromIter(iter, Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func fromIter(iter : Types.Iter<(K, V)>, compare : (implicit : (K, K) -> Order.Order)) : Map { - let map = empty(); - for ((key, value) in iter) { - add(map, compare, key, value) - }; - map - }; - - /// Converts an iterator of entries into a Map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = - /// Iter.fromArray([(0, "Zero"), (2, "Two"), (1, "One")]); - /// - /// let map = iter.toMap(Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func toMap(self : Types.Iter<(K, V)>, compare : (implicit : (K, K) -> Order.Order)) : Map { - fromIter(self, compare) - }; - - public func fromArray(array : [(K, V)], compare : (implicit : (K, K) -> Order.Order)) : Map { - fromIter(array.values(), compare) - }; - - public func fromVarArray(array : [var (K, V)], compare : (implicit : (K, K) -> Order.Order)) : Map { - fromIter(array.values(), compare) - }; - - /// Apply an operation on each key-value pair contained in the map. - /// The operation is applied in ascending order of the keys. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// var sum = 0; - /// var text = ""; - /// Map.forEach(map, func (key, value) { - /// sum += key; - /// text #= value; - /// }); - /// assert sum == 3; - /// assert text == "ZeroOneTwo"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func forEach(self : Map, operation : (K, V) -> ()) { - for (entry in entries(self)) { - operation(entry) - } - }; - - /// Filter entries in a new map. - /// Create a copy of the mutable map that only contains the key-value pairs - /// that fulfil the criterion function. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numberNames = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// let evenNames = Map.filter(numberNames, Nat.compare, func (key, value) { - /// key % 2 == 0 - /// }); - /// - /// assert Iter.toArray(Map.entries(evenNames)) == [(0, "Zero"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func filter(self : Map, compare : (implicit : (K, K) -> Order.Order), criterion : (K, V) -> Bool) : Map { - let result = empty(); - for ((key, value) in entries(self)) { - if (criterion(key, value)) { - add(result, compare, key, value) - } - }; - result - }; - - /// Project all values of the map in a new map. - /// Apply a mapping function to the values of each entry in the map and - /// collect the mapped entries in a new mutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func f(key : Nat, _val : Text) : Nat = key * 2; - /// - /// let resMap = Map.map(map, f); - /// - /// assert Iter.toArray(Map.entries(resMap)) == [(0, 0), (1, 2), (2, 4)]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func map(self : Map, project : (K, V1) -> V2) : Map { - { - var root = mapNode(self.root, project); - var size = self.size - } - }; - - /// Iterate all entries in ascending order of the keys, - /// and accumulate the entries by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func folder(accum : (Nat, Text), key : Nat, val : Text) : ((Nat, Text)) - /// = (key + accum.0, accum.1 # val); - /// - /// assert Map.foldLeft(map, (0, ""), folder) == (3, "ZeroOneTwo"); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func foldLeft( - self : Map, - base : A, - combine : (A, K, V) -> A - ) : A { - var accumulator = base; - for ((key, value) in entries(self)) { - accumulator := combine(accumulator, key, value) - }; - accumulator - }; - - /// Iterate all entries in descending order of the keys, - /// and accumulate the entries by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func folder(key : Nat, val : Text, accum : (Nat, Text)) : ((Nat, Text)) - /// = (key + accum.0, accum.1 # val); - /// - /// assert Map.foldRight(map, (0, ""), folder) == (3, "TwoOneZero"); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func foldRight( - self : Map, - base : A, - combine : (K, V, A) -> A - ) : A { - var accumulator = base; - for ((key, value) in reverseEntries(self)) { - accumulator := combine(key, value, accumulator) - }; - accumulator - }; - - /// Check whether all entries in the map fulfil a predicate function, i.e. - /// the predicate function returns `true` for all entries in the map. - /// Returns `true` for an empty map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "0"), (2, "2"), (1, "1")].values(), Nat.compare); - /// - /// assert Map.all(map, func (k, v) = v == Nat.toText(k)); - /// assert not Map.all(map, func (k, v) = k < 2); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func all(self : Map, predicate : (K, V) -> Bool) : Bool { - //TODO: optimize - for (entry in entries(self)) { - if (not predicate(entry)) { - return false - } - }; - true - }; - - /// Test if any key-value pair in `map` satisfies the given predicate `pred`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "0"), (2, "2"), (1, "1")].values(), Nat.compare); - /// - /// assert Map.any(map, func (k, v) = (k >= 0)); - /// assert not Map.any(map, func (k, v) = (k >= 3)); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func any(self : Map, predicate : (K, V) -> Bool) : Bool { - //TODO: optimize - for (entry in entries(self)) { - if (predicate(entry)) { - return true - } - }; - false - }; - - /// Filter all entries in the map by also applying a projection to the value. - /// Apply a mapping function `project` to all entries in the map and collect all - /// entries, for which the function returns a non-null new value. Collect all - /// non-discarded entries with the key and new value in a new mutable map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func f(key : Nat, val : Text) : ?Text { - /// if(key == 0) {null} - /// else { ?("Twenty " # val)} - /// }; - /// - /// let newMap = Map.filterMap(map, Nat.compare, f); - /// - /// assert Iter.toArray(Map.entries(newMap)) == [(1, "Twenty One"), (2, "Twenty Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func filterMap(self : Map, compare : (implicit : (K, K) -> Order.Order), project : (K, V1) -> ?V2) : Map { - let result = empty(); - for ((key, value1) in entries(self)) { - switch (project(key, value1)) { - case null {}; - case (?value2) add(result, compare, key, value2) - } - }; - result - }; - - /// Internal sanity check function. - /// Can be used to check that key/value pairs have been inserted with a consistent key comparison function. - /// Traps if the internal map structure is invalid. - /// @deprecated M0235 - public func assertValid(self : Map, compare : (implicit : (K, K) -> Order.Order)) { - func checkIteration(iterator : Types.Iter<(K, V)>, order : Order.Order) { - switch (iterator.next()) { - case null {}; - case (?first) { - var previous = first; - loop { - switch (iterator.next()) { - case null return; - case (?next) { - if (compare(previous.0, next.0) != order) { - Runtime.trap("Invalid order") - }; - previous := next - } - } - } - } - } - }; - checkIteration(entries(self), #less); - checkIteration(reverseEntries(self), #greater) - }; - - /// Generate a textual representation of all the entries in the map. - /// Primarily to be used for testing and debugging. - /// The keys and values are formatted according to `keyFormat` and `valueFormat`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// assert Map.toText(map, Nat.toText, func t { t }) == "Map{(0, Zero), (1, One), (2, Two)}"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that `keyFormat` and `valueFormat` have runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func toText(self : Map, keyFormat : (implicit : (toText : K -> Text)), valueFormat : (implicit : (toText : V -> Text))) : Text { - var text = "Map{"; - var sep = ""; - for ((key, value) in entries(self)) { - text #= sep # "(" # keyFormat(key) # ", " # valueFormat(value) # ")"; - sep := ", " - }; - text # "}" - }; - - /// Compare two maps by primarily comparing keys and secondarily values. - /// Both maps must have been created by the same key comparison function. - /// The two maps are iterated by the ascending order of their creation and - /// order is determined by the following rules: - /// Less: - /// `map1` is less than `map2` if: - /// * the pairwise iteration hits a entry pair `entry1` and `entry2` where - /// `entry1` is less than `entry2` and all preceding entry pairs are equal, or, - /// * `map1` is a strict prefix of `map2`, i.e. `map2` has more entries than `map1` - /// and all entries of `map1` occur at the beginning of iteration `map2`. - /// `entry1` is less than `entry2` if: - /// * the key of `entry1` is less than the key of `entry2`, or - /// * `entry1` and `entry2` have equal keys and the value of `entry1` is less than - /// the value of `entry2`. - /// Equal: - /// `map1` and `map2` have same series of equal entries by pairwise iteration. - /// Greater: - /// `map1` is neither less nor equal `map2`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/Map"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let map1 = Map.fromIter([(0, "Zero"), (1, "One")].values(), Nat.compare); - /// let map2 = Map.fromIter([(0, "Zero"), (2, "Two")].values(), Nat.compare); - /// - /// assert Map.compare(map1, map2, Nat.compare, Text.compare) == #less; - /// assert Map.compare(map1, map1, Nat.compare, Text.compare) == #equal; - /// assert Map.compare(map2, map1, Nat.compare, Text.compare) == #greater - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that `compareKey` and `compareValue` have runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func compare(self : Map, other : Map, compareKey : (implicit : (compare : (K, K) -> Order.Order)), compareValue : (implicit : (compare : (V, V) -> Order.Order))) : Order.Order { - let iterator1 = entries(self); - let iterator2 = entries(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?(key1, value1), ?(key2, value2)) { - let keyComparison = compareKey(key1, key2); - if (keyComparison != #equal) { - return keyComparison - }; - let valueComparison = compareValue(value1, value2); - if (valueComparison != #equal) { - return valueComparison - } - } - } - } - }; - - func leafEntries({ data } : Leaf) : Types.Iter<(K, V)> { - var i : Nat = 0; - object { - public func next() : ?(K, V) { - if (i >= data.count) { - null - } else { - let res = data.kvs[i]; - i += 1; - res - } - } - } - }; - - func leafEntriesFrom({ data } : Leaf, compare : (K, K) -> Order.Order, key : K) : Types.Iter<(K, V)> { - var i = switch (BinarySearch.binarySearchNode(data.kvs, compare, key, data.count)) { - case (#keyFound(i)) i; - case (#notFound(i)) i - }; - object { - public func next() : ?(K, V) { - if (i >= data.count) { - null - } else { - let res = data.kvs[i]; - i += 1; - res - } - } - } - }; - - func reverseLeafEntries({ data } : Leaf) : Types.Iter<(K, V)> { - var i : Nat = data.count; - object { - public func next() : ?(K, V) { - if (i == 0) { - null - } else { - let res = data.kvs[i - 1]; - i -= 1; - res - } - } - } - }; - - func reverseLeafEntriesFrom({ data } : Leaf, compare : (K, K) -> Order.Order, key : K) : Types.Iter<(K, V)> { - var i = switch (BinarySearch.binarySearchNode(data.kvs, compare, key, data.count)) { - case (#keyFound(i)) i + 1; // +1 to include this key - case (#notFound(i)) i // i is the index of the first key greater than the search key, or count if all keys are less than the search key - }; - object { - public func next() : ?(K, V) { - if (i == 0) { - null - } else { - let res = data.kvs[i - 1]; - i -= 1; - res - } - } - } - }; - - // Cursor type that keeps track of the current node and the current key-value index in the node - type NodeCursor = { node : Node; kvIndex : Nat }; - - func internalEntries(internal : Internal) : Types.Iter<(K, V)> { - // The nodeCursorStack keeps track of the current node and the current key-value index in the node - // We use a stack here to push to/pop off the next node cursor to visit - let nodeCursorStack = initializeForwardNodeCursorStack(internal); - internalEntriesFromStack(nodeCursorStack) - }; - - func internalEntriesFrom(internal : Internal, compare : (K, K) -> Order.Order, key : K) : Types.Iter<(K, V)> { - let nodeCursorStack = initializeForwardNodeCursorStackFrom(internal, compare, key); - internalEntriesFromStack(nodeCursorStack) - }; - - func internalEntriesFromStack(nodeCursorStack : Stack.Stack>) : Types.Iter<(K, V)> { - object { - public func next() : ?(K, V) { - // pop the next node cursor off the stack - var nodeCursor = Stack.pop(nodeCursorStack); - switch (nodeCursor) { - case null { return null }; - case (?{ node; kvIndex }) { - switch (node) { - // if a leaf node, iterate through the leaf node's next key-value pair - case (#leaf(leafNode)) { - let lastKV = leafNode.data.count - 1 : Nat; - if (kvIndex > lastKV) { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalEntries(), leaf kvIndex out of bounds") - }; - - let currentKV = switch (leafNode.data.kvs[kvIndex]) { - case (?kv) { kv }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Map.internalEntries(), null key-value pair found in leaf node." - # "leafNode.data.count=" # debug_show (leafNode.data.count) # ", kvIndex=" # debug_show (kvIndex) - ) - } - }; - // if not at the last key-value pair, push the next key-value index of the leaf onto the stack and return the current key-value pair - if (kvIndex < lastKV) { - Stack.push( - nodeCursorStack, - { - node = #leaf(leafNode); - kvIndex = kvIndex + 1 : Nat - } - ) - }; - - // return the current key-value pair - ?currentKV - }; - // if an internal node - case (#internal(internalNode)) { - let lastKV = internalNode.data.count - 1 : Nat; - // Developer facing message in case of a bug - if (kvIndex > lastKV) { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalEntries(), internal kvIndex out of bounds") - }; - - let currentKV = switch (internalNode.data.kvs[kvIndex]) { - case (?kv) { kv }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Map.internalEntries(), null key-value pair found in internal node. " # - "internal.data.count=" # debug_show (internalNode.data.count) # ", kvIndex=" # debug_show (kvIndex) - ) - } - }; - - let nextCursor = { - node = #internal(internalNode); - kvIndex = kvIndex + 1 : Nat - }; - // if not the last key-value pair, push the next key-value index of the internal node onto the stack - if (kvIndex < lastKV) { - Stack.push(nodeCursorStack, nextCursor) - }; - // traverse the next child's min subtree and push the resulting node cursors onto the stack - // then return the current key-value pair of the internal node - traverseMinSubtreeIter(nodeCursorStack, nextCursor); - ?currentKV - } - } - } - } - } - } - }; - - func reverseInternalEntries(internal : Internal) : Types.Iter<(K, V)> { - // The nodeCursorStack keeps track of the current node and the current key-value index in the node - // We use a stack here to push to/pop off the next node cursor to visit - let nodeCursorStack = initializeReverseNodeCursorStack(internal); - reverseInternalEntriesFromStack(nodeCursorStack) - }; - - func reverseInternalEntriesFrom(internal : Internal, compare : (K, K) -> Order.Order, key : K) : Types.Iter<(K, V)> { - let nodeCursorStack = initializeReverseNodeCursorStackFrom(internal, compare, key); - reverseInternalEntriesFromStack(nodeCursorStack) - }; - - func reverseInternalEntriesFromStack(nodeCursorStack : Stack.Stack>) : Types.Iter<(K, V)> { - object { - public func next() : ?(K, V) { - // pop the next node cursor off the stack - var nodeCursor = Stack.pop(nodeCursorStack); - switch (nodeCursor) { - case null { return null }; - case (?{ node; kvIndex }) { - let firstKV = 0 : Nat; - assert (kvIndex > firstKV); - switch (node) { - // if a leaf node, reverse iterate through the leaf node's next key-value pair - case (#leaf(leafNode)) { - let currentKV = switch (leafNode.data.kvs[kvIndex - 1]) { - case (?kv) { kv }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Map.reverseInternalEntries(), null key-value pair found in leaf node." - # "leafNode.data.count=" # debug_show (leafNode.data.count) # ", kvIndex=" # debug_show (kvIndex) - ) - } - }; - // if not at the last key-value pair, push the previous key-value index of the leaf onto the stack and return the current key-value pair - if (kvIndex - 1 : Nat > firstKV) { - Stack.push( - nodeCursorStack, - { - node = #leaf(leafNode); - kvIndex = kvIndex - 1 : Nat - } - ) - }; - - // return the current key-value pair - ?currentKV - }; - // if an internal node - case (#internal(internalNode)) { - let currentKV = switch (internalNode.data.kvs[kvIndex - 1]) { - case (?kv) { kv }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Map.reverseInternalEntries(), null key-value pair found in internal node. " # - "internal.data.count=" # debug_show (internalNode.data.count) # ", kvIndex=" # debug_show (kvIndex) - ) - } - }; - - let previousCursor = { - node = #internal(internalNode); - kvIndex = kvIndex - 1 : Nat - }; - // if not the first key-value pair, push the previous key-value index of the internal node onto the stack - if (kvIndex - 1 : Nat > firstKV) { - Stack.push(nodeCursorStack, previousCursor) - }; - // traverse the previous child's max subtree and push the resulting node cursors onto the stack - // then return the current key-value pair of the internal node - traverseMaxSubtreeIter(nodeCursorStack, previousCursor); - ?currentKV - } - } - } - } - } - } - }; - - func initializeForwardNodeCursorStack(internal : Internal) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - kvIndex = 0 - }; - - // push the initial cursor to the stack - Stack.push(nodeCursorStack, nodeCursor); - // then traverse left - traverseMinSubtreeIter(nodeCursorStack, nodeCursor); - nodeCursorStack - }; - - func initializeForwardNodeCursorStackFrom(internal : Internal, compare : (K, K) -> Order.Order, key : K) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - kvIndex = 0 - }; - - traverseMinSubtreeIterFrom(nodeCursorStack, nodeCursor, compare, key); - nodeCursorStack - }; - - func initializeReverseNodeCursorStack(internal : Internal) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - kvIndex = internal.data.count - }; - - // push the initial cursor to the stack - Stack.push(nodeCursorStack, nodeCursor); - // then traverse left - traverseMaxSubtreeIter(nodeCursorStack, nodeCursor); - nodeCursorStack - }; - - func initializeReverseNodeCursorStackFrom(internal : Internal, compare : (K, K) -> Order.Order, key : K) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - kvIndex = internal.data.count - }; - - traverseMaxSubtreeIterFrom(nodeCursorStack, nodeCursor, compare, key); - nodeCursorStack - }; - - // traverse the min subtree of the current node cursor, passing each new element to the node cursor stack - func traverseMinSubtreeIter(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor) { - var currentNode = nodeCursor.node; - var childIndex = nodeCursor.kvIndex; - - label l loop { - switch (currentNode) { - // If currentNode is leaf, have hit the minimum element of the subtree and already pushed it's cursor to the stack - // so can return - case (#leaf(_)) { - return - }; - // If currentNode is internal, add it's left most child to the stack and continue traversing - case (#internal(internalNode)) { - switch (internalNode.children[childIndex]) { - // Push the next min (left most) child node to the stack - case (?childNode) { - childIndex := 0; - currentNode := childNode; - Stack.push( - nodeCursorStack, - { - node = currentNode; - kvIndex = childIndex - } - ) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.traverseMinSubtreeIter(), null child node error") - } - } - } - } - } - }; - - func traverseMinSubtreeIterFrom(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor, compare : (K, K) -> Order.Order, key : K) { - var currentNode = nodeCursor.node; - - label l loop { - let (node, childrenOption) = switch (currentNode) { - case (#leaf(leafNode)) (leafNode, null); - case (#internal(internalNode)) (internalNode, ?internalNode.children) - }; - let (i, isFound) = switch (NodeUtil.getKeyIndex(node.data, compare, key)) { - case (#keyFound(i)) (i, true); - case (#notFound(i)) (i, false) - }; - if (i < node.data.count) { - Stack.push( - nodeCursorStack, - { - node = currentNode; - kvIndex = i // greater entries to traverse - } - ) - }; - if isFound return; - let ?children = childrenOption else return; - let ?childNode = children[i] else Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.traverseMinSubtreeIterFrom(), null child node error"); - currentNode := childNode - } - }; - - // traverse the max subtree of the current node cursor, passing each new element to the node cursor stack - func traverseMaxSubtreeIter(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor) { - var currentNode = nodeCursor.node; - var childIndex = nodeCursor.kvIndex; - - label l loop { - switch (currentNode) { - // If currentNode is leaf, have hit the maximum element of the subtree and already pushed it's cursor to the stack - // so can return - case (#leaf(_)) { - return - }; - // If currentNode is internal, add it's right most child to the stack and continue traversing - case (#internal(internalNode)) { - assert (childIndex <= internalNode.data.count); // children are one more than data entries - switch (internalNode.children[childIndex]) { - // Push the next max (right most) child node to the stack - case (?childNode) { - childIndex := switch (childNode) { - case (#internal(internalNode)) internalNode.data.count; - case (#leaf(leafNode)) leafNode.data.count - }; - currentNode := childNode; - Stack.push( - nodeCursorStack, - { - node = currentNode; - kvIndex = childIndex - } - ) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.traverseMaxSubtreeIter(), null child node error") - } - } - } - } - } - }; - - func traverseMaxSubtreeIterFrom(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor, compare : (K, K) -> Order.Order, key : K) { - var currentNode = nodeCursor.node; - - label l loop { - let (node, childrenOption) = switch (currentNode) { - case (#leaf(leafNode)) (leafNode, null); - case (#internal(internalNode)) (internalNode, ?internalNode.children) - }; - let (i, isFound) = switch (NodeUtil.getKeyIndex(node.data, compare, key)) { - case (#keyFound(i)) (i + 1, true); // +1 to include this key - case (#notFound(i)) (i, false) // i is the index of the first key less than the search key, or 0 if all keys are greater than the search key - }; - if (i > 0) { - Stack.push( - nodeCursorStack, - { - node = currentNode; - kvIndex = i - } - ) - }; - if isFound return; - let ?children = childrenOption else return; - let ?childNode = children[i] else Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.traverseMaxSubtreeIterFrom(), null child node error"); - currentNode := childNode - } - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateInternalDeleteResult = { - // element was deleted or not found, returning the old value (?value or null) - #delete : ?V; - // deleted an element, but was unable to successfully borrow and rebalance at the previous level without merging children - // the internalChild is the merged child that needs to be rebalanced at the next level up in the BTree - #mergeChild : { - internalChild : Internal; - deletedValue : ?V - } - }; - - func internalDeleteHelper(internalNode : Internal, order : Nat, compare : (K, K) -> Order.Order, deleteKey : K, skipNode : Bool) : IntermediateInternalDeleteResult { - let minKeys = NodeUtil.minKeysFromOrder(order); - let keyIndex = NodeUtil.getKeyIndex(internalNode.data, compare, deleteKey); - - // match on both the result of the node binary search, and if this node level should be skipped even if the key is found (internal kv replacement case) - switch (keyIndex, skipNode) { - // if key is found in the internal node - case (#keyFound(deleteIndex), false) { - let deletedValue = switch (internalNode.data.kvs[deleteIndex]) { - case (?kv) { ?kv.1 }; - case null { assert false; null } - }; - // TODO: (optimization) replace with deletion in one step without having to retrieve the maxKey first - let replaceKV = NodeUtil.getMaxKeyValue(internalNode.children[deleteIndex]); - internalNode.data.kvs[deleteIndex] := ?replaceKV; - switch (internalDeleteHelper(internalNode, order, compare, replaceKV.0, true)) { - case (#delete(_)) { #delete(deletedValue) }; - case (#mergeChild({ internalChild })) { - #mergeChild({ internalChild; deletedValue }) - } - } - }; - // if key is not found in the internal node OR the key is found, but skipping this node (because deleting the in order precessor i.e. replacement kv) - // in both cases need to descend and traverse to find the kv to delete - case ((#keyFound(_), true) or (#notFound(_), _)) { - let childIndex = switch (keyIndex) { - case (#keyFound(replacedSkipKeyIndex)) { replacedSkipKeyIndex }; - case (#notFound(childIndex)) { childIndex } - }; - let child = switch (internalNode.children[childIndex]) { - case (?c) { c }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalDeleteHelper, child index of #keyFound or #notfound is null") - } - }; - switch (child) { - // if child is internal - case (#internal(internalChild)) { - switch (internalDeleteHelper(internalChild, order, compare, deleteKey, false), childIndex == 0) { - // if value was successfully deleted and no additional tree re-balancing is needed, return the deleted value - case (#delete(v), _) { #delete(v) }; - // if internalChild needs rebalancing and pulling child is left most - case (#mergeChild({ internalChild; deletedValue }), true) { - // try to pull left-most key and child from right sibling - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex + 1, #successor)) { - // if can pull up sibling kv and child - case (#borrowed({ deletedSiblingKVPair; child })) { - NodeUtil.rotateBorrowedKVsAndChildFromSibling( - internalNode, - childIndex, - deletedSiblingKVPair, - child, - internalChild, - #right - ); - #delete(deletedValue) - }; - // unable to pull from sibling, need to merge with right sibling and push down parent - case (#notEnoughKeys(sibling)) { - // get the parent kv that will be pushed down the the child - let kvPairToBePushedToChild = ?BTreeHelper.deleteAndShift(internalNode.data.kvs, 0); - internalNode.data.count -= 1; - // merge the children and push down the parent - let newChild = NodeUtil.mergeChildrenAndPushDownParent(internalChild, kvPairToBePushedToChild, sibling); - // update children of the parent - internalNode.children[0] := ?#internal(newChild); - ignore ?BTreeHelper.deleteAndShift(internalNode.children, 1); - - if (internalNode.data.count < minKeys) { - #mergeChild({ internalChild = internalNode; deletedValue }) - } else { - #delete(deletedValue) - } - } - } - }; - // if internalChild needs rebalancing and pulling child is > 0, so a left sibling exists - case (#mergeChild({ internalChild; deletedValue }), false) { - // try to pull right-most key and its child directly from left sibling - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex - 1 : Nat, #predecessor)) { - case (#borrowed({ deletedSiblingKVPair; child })) { - NodeUtil.rotateBorrowedKVsAndChildFromSibling( - internalNode, - childIndex - 1 : Nat, - deletedSiblingKVPair, - child, - internalChild, - #left - ); - #delete(deletedValue) - }; - // unable to pull from left sibling - case (#notEnoughKeys(leftSibling)) { - // if child is not last index, try to pull from the right child - if (childIndex < internalNode.data.count) { - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex, #successor)) { - // if can pull up sibling kv and child - case (#borrowed({ deletedSiblingKVPair; child })) { - NodeUtil.rotateBorrowedKVsAndChildFromSibling( - internalNode, - childIndex, - deletedSiblingKVPair, - child, - internalChild, - #right - ); - return #delete(deletedValue) - }; - // if cannot borrow, from left or right, merge (see below) - case _ {} - } - }; - - // get the parent kv that will be pushed down the the child - let kvPairToBePushedToChild = ?BTreeHelper.deleteAndShift(internalNode.data.kvs, childIndex - 1 : Nat); - internalNode.data.count -= 1; - // merge it the children and push down the parent - let newChild = NodeUtil.mergeChildrenAndPushDownParent(leftSibling, kvPairToBePushedToChild, internalChild); - - // update children of the parent - internalNode.children[childIndex - 1] := ?#internal(newChild); - ignore ?BTreeHelper.deleteAndShift(internalNode.children, childIndex); - - if (internalNode.data.count < minKeys) { - #mergeChild({ internalChild = internalNode; deletedValue }) - } else { - #delete(deletedValue) - } - } - } - } - } - }; - // if child is leaf - case (#leaf(leafChild)) { - switch (leafDeleteHelper(leafChild, order, compare, deleteKey), childIndex == 0) { - case (#delete(value), _) { #delete(value) }; - // if delete child is left most, try to borrow from right child - case (#mergeLeafData({ leafDeleteIndex }), true) { - switch (NodeUtil.borrowFromRightLeafChild(internalNode.children, childIndex)) { - case (?borrowedKVPair) { - let kvPairToBePushedToChild = internalNode.data.kvs[childIndex]; - internalNode.data.kvs[childIndex] := ?borrowedKVPair; - - let deletedKV = BTreeHelper.insertAtPostionAndDeleteAtPosition<(K, V)>(leafChild.data.kvs, kvPairToBePushedToChild, leafChild.data.count - 1, leafDeleteIndex); - #delete(?deletedKV.1) - }; - - case null { - // can't borrow from right child, delete from leaf and merge with right child and parent kv, then push down into new leaf - let rightChild = switch (internalNode.children[childIndex + 1]) { - case (?#leaf(rc)) { rc }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalDeleteHelper, if trying to borrow from right leaf child is null, rightChild index cannot be null or internal") - } - }; - let (mergedLeaf, deletedKV) = mergeParentWithLeftRightChildLeafNodesAndDelete( - internalNode.data.kvs[childIndex], - leafChild, - rightChild, - leafDeleteIndex, - #left - ); - // delete the left most internal node kv, since was merging from a deletion in left most child (0) and the parent kv was pushed into the mergedLeaf - ignore BTreeHelper.deleteAndShift<(K, V)>(internalNode.data.kvs, 0); - // update internal node children - BTreeHelper.replaceTwoWithElementAndShift>(internalNode.children, #leaf(mergedLeaf), 0); - internalNode.data.count -= 1; - - if (internalNode.data.count < minKeys) { - #mergeChild({ - internalChild = internalNode; - deletedValue = ?deletedKV.1 - }) - } else { - #delete(?deletedKV.1) - } - - } - } - }; - // if delete child is middle or right most, try to borrow from left child - case (#mergeLeafData({ leafDeleteIndex }), false) { - // if delete child is right most, try to borrow from left child - switch (NodeUtil.borrowFromLeftLeafChild(internalNode.children, childIndex)) { - case (?borrowedKVPair) { - let kvPairToBePushedToChild = internalNode.data.kvs[childIndex - 1]; - internalNode.data.kvs[childIndex - 1] := ?borrowedKVPair; - let kvDelete = BTreeHelper.insertAtPostionAndDeleteAtPosition<(K, V)>(leafChild.data.kvs, kvPairToBePushedToChild, 0, leafDeleteIndex); - #delete(?kvDelete.1) - }; - case null { - // if delete child is in the middle, try to borrow from right child - if (childIndex < internalNode.data.count) { - // try to borrow from right - switch (NodeUtil.borrowFromRightLeafChild(internalNode.children, childIndex)) { - case (?borrowedKVPair) { - let kvPairToBePushedToChild = internalNode.data.kvs[childIndex]; - internalNode.data.kvs[childIndex] := ?borrowedKVPair; - // insert the successor at the very last element - let kvDelete = BTreeHelper.insertAtPostionAndDeleteAtPosition<(K, V)>(leafChild.data.kvs, kvPairToBePushedToChild, leafChild.data.count - 1, leafDeleteIndex); - return #delete(?kvDelete.1) - }; - // if cannot borrow, from left or right, merge (see below) - case _ {} - } - }; - - // can't borrow from left child, delete from leaf and merge with left child and parent kv, then push down into new leaf - let leftChild = switch (internalNode.children[childIndex - 1]) { - case (?#leaf(lc)) { lc }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.internalDeleteHelper, if trying to borrow from left leaf child is null, then left child index must not be null or internal") - } - }; - let (mergedLeaf, deletedKV) = mergeParentWithLeftRightChildLeafNodesAndDelete( - internalNode.data.kvs[childIndex - 1], - leftChild, - leafChild, - leafDeleteIndex, - #right - ); - // delete the right most internal node kv, since was merging from a deletion in the right most child and the parent kv was pushed into the mergedLeaf - ignore BTreeHelper.deleteAndShift<(K, V)>(internalNode.data.kvs, childIndex - 1); - // update internal node children - BTreeHelper.replaceTwoWithElementAndShift>(internalNode.children, #leaf(mergedLeaf), childIndex - 1); - internalNode.data.count -= 1; - - if (internalNode.data.count < minKeys) { - #mergeChild({ - internalChild = internalNode; - deletedValue = ?deletedKV.1 - }) - } else { - #delete(?deletedKV.1) - } - } - } - } - } - } - } - } - } - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateLeafDeleteResult = { - // element was deleted or not found, returning the old value (?value or null) - #delete : ?V; - // leaf had the minimum number of keys when deleting, so returns the leaf node's data and the index of the key that will be deleted - #mergeLeafData : { - data : Data; - leafDeleteIndex : Nat - } - }; - - func leafDeleteHelper(leafNode : Leaf, order : Nat, compare : (K, K) -> Order.Order, deleteKey : K) : IntermediateLeafDeleteResult { - let minKeys = NodeUtil.minKeysFromOrder(order); - - switch (NodeUtil.getKeyIndex(leafNode.data, compare, deleteKey)) { - case (#keyFound(deleteIndex)) { - if (leafNode.data.count > minKeys) { - leafNode.data.count -= 1; - #delete(?BTreeHelper.deleteAndShift<(K, V)>(leafNode.data.kvs, deleteIndex).1) - } else { - #mergeLeafData({ - data = leafNode.data; - leafDeleteIndex = deleteIndex - }) - } - }; - case (#notFound(_)) { - #delete(null) - } - } - }; - - // get helper if internal node - func getFromInternal(internalNode : Internal, compare : (K, K) -> Order.Order, key : K) : ?V { - switch (NodeUtil.getKeyIndex(internalNode.data, compare, key)) { - case (#keyFound(index)) { - getExistingValueFromIndex(internalNode.data, index) - }; - case (#notFound(index)) { - switch (internalNode.children[index]) { - // expects the child to be there, otherwise there's a bug in binary search or the tree is invalid - case null { Runtime.trap("Internal bug: Map.getFromInternal") }; - case (?#leaf(leafNode)) { getFromLeaf(leafNode, compare, key) }; - case (?#internal(internalNode)) { - getFromInternal(internalNode, compare, key) - } - } - } - } - }; - - // get function helper if leaf node - func getFromLeaf(leafNode : Leaf, compare : (K, K) -> Order.Order, key : K) : ?V { - switch (NodeUtil.getKeyIndex(leafNode.data, compare, key)) { - case (#keyFound(index)) { - getExistingValueFromIndex(leafNode.data, index) - }; - case _ null - } - }; - - // get function helper that retrieves an existing value in the case that the key is found - func getExistingValueFromIndex(data : Data, index : Nat) : ?V { - switch (data.kvs[index]) { - case null { null }; - case (?ov) { ?ov.1 } - } - }; - - // which child the deletionIndex is referring to - type DeletionSide = { #left; #right }; - - func mergeParentWithLeftRightChildLeafNodesAndDelete( - parentKV : ?(K, V), - leftChild : Leaf, - rightChild : Leaf, - deleteIndex : Nat, - deletionSide : DeletionSide - ) : (Leaf, (K, V)) { - let count = leftChild.data.count * 2; - let (kvs, deletedKV) = BTreeHelper.mergeParentWithChildrenAndDelete<(K, V)>( - parentKV, - leftChild.data.count, - leftChild.data.kvs, - rightChild.data.kvs, - deleteIndex, - deletionSide - ); - ( - { - data = { - kvs; - var count = count - } - }, - deletedKV - ) - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateInsertResult = { - // element was inserted or replaced, returning the old value (?value or null) - #insert : ?V; - // child was full when inserting, so returns the promoted kv pair and the split left and right child - #promote : { - kv : (K, V); - leftChild : Node; - rightChild : Node - } - }; - - // Helper for inserting into a leaf node - func leafInsertHelper(leafNode : Leaf, order : Nat, compare : (K, K) -> Order.Order, key : K, value : V) : (IntermediateInsertResult) { - // Perform binary search to see if the element exists in the node - switch (NodeUtil.getKeyIndex(leafNode.data, compare, key)) { - case (#keyFound(insertIndex)) { - let previous = leafNode.data.kvs[insertIndex]; - leafNode.data.kvs[insertIndex] := ?(key, value); - switch (previous) { - case (?ov) { #insert(?ov.1) }; - case null { assert false; #insert(null) }; // the binary search already found an element, so this case should never happen - } - }; - case (#notFound(insertIndex)) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - let maxKeys : Nat = order - 1; - // If the leaf is full, insert, split the node, and promote the middle element - if (leafNode.data.count >= maxKeys) { - let (leftKVs, promotedParentElement, rightKVs) = BTreeHelper.insertOneAtIndexAndSplitArray( - leafNode.data.kvs, - (key, value), - insertIndex - ); - - let leftCount = order / 2; - let rightCount : Nat = if (order % 2 == 0) { leftCount - 1 } else { - leftCount - }; - - ( - #promote({ - kv = promotedParentElement; - leftChild = createLeaf(leftKVs, leftCount); - rightChild = createLeaf(rightKVs, rightCount) - }) - ) - } - // Otherwise, insert at the specified index (shifting elements over if necessary) - else { - NodeUtil.insertAtIndexOfNonFullNodeData(leafNode.data, ?(key, value), insertIndex); - #insert(null) - } - } - } - }; - - // Helper for inserting into an internal node - func internalInsertHelper(internalNode : Internal, order : Nat, compare : (K, K) -> Order.Order, key : K, value : V) : IntermediateInsertResult { - switch (NodeUtil.getKeyIndex(internalNode.data, compare, key)) { - case (#keyFound(insertIndex)) { - let previous = internalNode.data.kvs[insertIndex]; - internalNode.data.kvs[insertIndex] := ?(key, value); - switch (previous) { - case (?ov) { #insert(?ov.1) }; - case null { assert false; #insert(null) }; // the binary search already found an element, so this case should never happen - } - }; - case (#notFound(insertIndex)) { - let insertResult = switch (internalNode.children[insertIndex]) { - case null { assert false; #insert(null) }; - case (?#leaf(leafNode)) { - leafInsertHelper(leafNode, order, compare, key, value) - }; - case (?#internal(internalChildNode)) { - internalInsertHelper(internalChildNode, order, compare, key, value) - } - }; - - switch (insertResult) { - case (#insert(ov)) { #insert(ov) }; - case (#promote({ kv; leftChild; rightChild })) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - let maxKeys : Nat = order - 1; - // if current internal node is full, need to split the internal node - if (internalNode.data.count >= maxKeys) { - // insert and split internal kvs, determine new promotion target kv - let (leftKVs, promotedParentElement, rightKVs) = BTreeHelper.insertOneAtIndexAndSplitArray( - internalNode.data.kvs, - (kv), - insertIndex - ); - - // calculate the element count in the left KVs and the element count in the right KVs - let leftCount = order / 2; - let rightCount : Nat = if (order % 2 == 0) { leftCount - 1 } else { - leftCount - }; - - // split internal children - let (leftChildren, rightChildren) = NodeUtil.splitChildrenInTwoWithRebalances( - internalNode.children, - insertIndex, - leftChild, - rightChild - ); - - // send the kv to be promoted, as well as the internal children left and right split - #promote({ - kv = promotedParentElement; - leftChild = #internal({ - data = { kvs = leftKVs; var count = leftCount }; - children = leftChildren - }); - rightChild = #internal({ - data = { kvs = rightKVs; var count = rightCount }; - children = rightChildren - }) - }) - } else { - // insert the new kvs into the internal node - NodeUtil.insertAtIndexOfNonFullNodeData(internalNode.data, ?kv, insertIndex); - // split and re-insert the single child that needs rebalancing - NodeUtil.insertRebalancedChild(internalNode.children, insertIndex, leftChild, rightChild); - #insert(null) - } - } - } - } - } - }; - - func createLeaf(kvs : [var ?(K, V)], count : Nat) : Node { - #leaf({ - data = { - kvs; - var count - } - }) - }; - - // Additional functionality compared to original source. - - func mapData(data : Data, project : (K, V1) -> V2) : Data { - { - kvs = VarArray.map( - data.kvs, - func entry { - switch entry { - case (?kv) ?(kv.0, project kv); - case null null - } - } - ); - var count = data.count - } - }; - - func mapNode(node : Node, project : (K, V1) -> V2) : Node { - switch node { - case (#leaf { data }) { - #leaf { data = mapData(data, project) } - }; - case (#internal { data; children }) { - let mappedData = mapData(data, project); - let mappedChildren = VarArray.map, ?Node>( - children, - func child { - switch child { - case null null; - case (?childNode) ?mapNode(childNode, project) - } - } - ); - # internal({ - data = mappedData; - children = mappedChildren - }) - } - } - }; - - func cloneNode(node : Node) : Node = mapNode(node, func(k, v) = v); - - module BinarySearch { - public type SearchResult = { - #keyFound : Nat; - #notFound : Nat - }; - - /// Searches an array for a specific key, returning the index it occurs at if #keyFound, or the child/insert index it may occur at - /// if #notFound. This is used when determining if a key exists in an internal or leaf node, where a key should be inserted in a - /// leaf node, or which child of an internal node a key could be in. - /// - /// Note: This function expects a mutable, nullable, array of keys in sorted order, where all nulls appear at the end of the array. - /// This function may trap if a null value appears before any values. It also expects a maxIndex, which is the right-most index (bound) - /// from which to begin the binary search (the left most bound is expected to be 0) - /// - /// Parameters: - /// - /// * array - the sorted array that the binary search is performed upon - /// * compare - the comparator used to perform the search - /// * searchKey - the key being compared against in the search - /// * maxIndex - the right-most index (bound) from which to begin the search - public func binarySearchNode(array : [var ?(K, V)], compare : (implicit : (K, K) -> Order.Order), searchKey : K, maxIndex : Nat) : SearchResult { - // TODO: get rid of this check? - // Trap if array is size 0 (should not happen) - if (array.size() == 0) { - assert false - }; - - // if all elements in the array are null (i.e. first element is null), return #notFound(0) - if (maxIndex == 0) { - return #notFound(0) - }; - - // Initialize search from first to last index - var left : Nat = 0; - var right = maxIndex; // maxIndex does not necessarily mean array.size() - 1 - // Search the array - while (left < right) { - let middle = (left + right) / 2; - switch (array[middle]) { - case null { assert false }; - case (?(key, _)) { - switch (compare(searchKey, key)) { - // If the element is present at the middle itself - case (#equal) { return #keyFound(middle) }; - // If element is greater than mid, it can only be present in left subarray - case (#greater) { left := middle + 1 }; - // If element is smaller than mid, it can only be present in right subarray - case (#less) { - right := if (middle == 0) { 0 } else { middle - 1 } - } - } - } - } - }; - - if (left == array.size()) { - return #notFound(left) - }; - - // left == right - switch (array[left]) { - // inserting at end of array - case null { #notFound(left) }; - case (?(key, _)) { - switch (compare(searchKey, key)) { - // if left is the key - case (#equal) { #keyFound(left) }; - // if the key is not found, return notFound and the insert location - case (#greater) { #notFound(left + 1) }; - case (#less) { #notFound(left) } - } - } - } - } - }; - - module NodeUtil { - /// Inserts element at the given index into a non-full leaf node - public func insertAtIndexOfNonFullNodeData(data : Data, kvPair : ?(K, V), insertIndex : Nat) { - let currentLastElementIndex : Nat = if (data.count == 0) { 0 } else { - data.count - 1 - }; - BTreeHelper.insertAtPosition<(K, V)>(data.kvs, kvPair, insertIndex, currentLastElementIndex); - - // increment the count of data in this node since just inserted an element - data.count += 1 - }; - - /// Inserts two rebalanced (split) child halves into a non-full array of children. - public func insertRebalancedChild(children : [var ?Node], rebalancedChildIndex : Nat, leftChildInsert : Node, rightChildInsert : Node) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - var j : Nat = children.size() - 2; - - // This is just a sanity check to ensure the children aren't already full (should split promote otherwise) - // TODO: Remove this check once confident - if (Option.isSome(children[j + 1])) { assert false }; - - // Iterate backwards over the array and shift each element over to the right by one until the rebalancedChildIndex is hit - while (j > rebalancedChildIndex) { - children[j + 1] := children[j]; - j -= 1 - }; - - // Insert both the left and right rebalanced children (replacing the pre-split child) - children[j] := ?leftChildInsert; - children[j + 1] := ?rightChildInsert - }; - - /// Used when splitting the children of an internal node - /// - /// Takes in the rebalanced child index, as well as both halves of the rebalanced child and splits the children, inserting the left and right child halves appropriately - /// - /// For more context, see the documentation for the splitArrayAndInsertTwo method in BTreeHelper.mo - public func splitChildrenInTwoWithRebalances( - children : [var ?Node], - rebalancedChildIndex : Nat, - leftChildInsert : Node, - rightChildInsert : Node - ) : ([var ?Node], [var ?Node]) { - BTreeHelper.splitArrayAndInsertTwo>(children, rebalancedChildIndex, leftChildInsert, rightChildInsert) - }; - - /// Helper used to get the key index of of a key within a node - /// - /// for more, see the BinarySearch.binarySearchNode() documentation - public func getKeyIndex(data : Data, compare : (K, K) -> Order.Order, key : K) : BinarySearch.SearchResult { - BinarySearch.binarySearchNode(data.kvs, compare, key, data.count) - }; - - // calculates a BTree Node's minimum allowed keys given the order of the BTree - public func minKeysFromOrder(order : Nat) : Nat { - if (order % 2 == 0) { order / 2 - 1 } else { order / 2 } - }; - - // Given a node, get the maximum key value (right most leaf kv) - public func getMaxKeyValue(node : ?Node) : (K, V) { - switch (node) { - case (?#leaf({ data })) { - switch (data.kvs[data.count - 1]) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.NodeUtil.getMaxKeyValue, data cannot have more elements than it's count") - }; - case (?kv) { kv } - } - }; - case (?#internal({ data; children })) { - getMaxKeyValue(children[data.count]) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.NodeUtil.getMaxKeyValue, the node provided cannot be null") - } - } - }; - - type InorderBorrowType = { - #predecessor; - #successor - }; - - // attempts to retrieve the in max key of the child leaf node directly to the left if the node will allow it - // returns the deleted max key if able to retrieve, null if not able - // - // mutates the predecessing node's keys - public func borrowFromLeftLeafChild(children : [var ?Node], ofChildIndex : Nat) : ?(K, V) { - let predecessorIndex : Nat = ofChildIndex - 1; - borrowFromLeafChild(children, predecessorIndex, #predecessor) - }; - - // attempts to retrieve the in max key of the child leaf node directly to the right if the node will allow it - // returns the deleted max key if able to retrieve, null if not able - // - // mutates the predecessing node's keys - public func borrowFromRightLeafChild(children : [var ?Node], ofChildIndex : Nat) : ?(K, V) { - borrowFromLeafChild(children, ofChildIndex + 1, #successor) - }; - - func borrowFromLeafChild(children : [var ?Node], borrowChildIndex : Nat, childSide : InorderBorrowType) : ?(K, V) { - let minKeys = minKeysFromOrder(children.size()); - - switch (children[borrowChildIndex]) { - case (?#leaf({ data })) { - if (data.count > minKeys) { - // able to borrow a key-value from this child, so decrement the count of kvs - data.count -= 1; // Since enforce order >= 4, there will always be at least 1 element per node - switch (childSide) { - case (#predecessor) { - let deletedKV = data.kvs[data.count]; - data.kvs[data.count] := null; - deletedKV - }; - case (#successor) { - ?BTreeHelper.deleteAndShift(data.kvs, 0) - } - } - } else { null } - }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.NodeUtil.borrowFromLeafChild, the node at the borrow child index cannot be null or internal") - } - } - }; - - type InternalBorrowResult = { - #borrowed : InternalBorrow; - #notEnoughKeys : Internal - }; - - type InternalBorrow = { - deletedSiblingKVPair : ?(K, V); - child : ?Node - }; - - // Attempts to borrow a KV and child from an internal sibling node - public func borrowFromInternalSibling(children : [var ?Node], borrowChildIndex : Nat, borrowType : InorderBorrowType) : InternalBorrowResult { - let minKeys = minKeysFromOrder(children.size()); - - switch (children[borrowChildIndex]) { - case (?#internal({ data; children })) { - if (data.count > minKeys) { - data.count -= 1; - switch (borrowType) { - case (#predecessor) { - let deletedSiblingKVPair = data.kvs[data.count]; - data.kvs[data.count] := null; - let child = children[data.count + 1]; - children[data.count + 1] := null; - #borrowed({ - deletedSiblingKVPair; - child - }) - }; - case (#successor) { - #borrowed({ - deletedSiblingKVPair = ?BTreeHelper.deleteAndShift(data.kvs, 0); - child = ?BTreeHelper.deleteAndShift(children, 0) - }) - } - } - } else { #notEnoughKeys({ data; children }) } - }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Map.NodeUtil.borrowFromInternalSibling from internal sibling, the child at the borrow index cannot be null or a leaf") - } - } - }; - - type SiblingSide = { #left; #right }; - - // Rotates the borrowed KV and child from sibling side of the internal node to the internal child recipient - public func rotateBorrowedKVsAndChildFromSibling( - internalNode : Internal, - parentRotateIndex : Nat, - borrowedSiblingKVPair : ?(K, V), - borrowedSiblingChild : ?Node, - internalChildRecipient : Internal, - siblingSide : SiblingSide - ) { - // if borrowing from the left, the rotated key and child will always be inserted first - // if borrowing from the right, the rotated key and child will always be inserted last - let (kvIndex, childIndex) = switch (siblingSide) { - case (#left) { (0, 0) }; - case (#right) { - (internalChildRecipient.data.count, internalChildRecipient.data.count + 1) - } - }; - - // get the parent kv that will be pushed down the the child - let kvPairToBePushedToChild = internalNode.data.kvs[parentRotateIndex]; - // replace the parent with the sibling kv - internalNode.data.kvs[parentRotateIndex] := borrowedSiblingKVPair; - // push the kv and child down into the internalChild - insertAtIndexOfNonFullNodeData(internalChildRecipient.data, kvPairToBePushedToChild, kvIndex); - - BTreeHelper.insertAtPosition>(internalChildRecipient.children, borrowedSiblingChild, childIndex, internalChildRecipient.data.count) - }; - - // Merges the kvs and children of two internal nodes, pushing the parent kv in between the right and left halves - public func mergeChildrenAndPushDownParent(leftChild : Internal, parentKV : ?(K, V), rightChild : Internal) : Internal { - { - data = mergeData(leftChild.data, parentKV, rightChild.data); - children = mergeChildren(leftChild.children, rightChild.children) - } - }; - - func mergeData(leftData : Data, parentKV : ?(K, V), rightData : Data) : Data { - assert leftData.count <= minKeysFromOrder(leftData.kvs.size() + 1); - assert rightData.count <= minKeysFromOrder(rightData.kvs.size() + 1); - - let mergedKVs = VarArray.repeat(null, leftData.kvs.size()); - var i = 0; - while (i < leftData.count) { - mergedKVs[i] := leftData.kvs[i]; - i += 1 - }; - - mergedKVs[i] := parentKV; - i += 1; - - var j = 0; - while (j < rightData.count) { - mergedKVs[i] := rightData.kvs[j]; - i += 1; - j += 1 - }; - - { - kvs = mergedKVs; - var count = leftData.count + 1 + rightData.count - } - }; - - func mergeChildren(leftChildren : [var ?Node], rightChildren : [var ?Node]) : [var ?Node] { - let mergedChildren = VarArray.repeat>(null, leftChildren.size()); - var i = 0; - - while (Option.isSome(leftChildren[i])) { - mergedChildren[i] := leftChildren[i]; - i += 1 - }; - - var j = 0; - while (Option.isSome(rightChildren[j])) { - mergedChildren[i] := rightChildren[j]; - i += 1; - j += 1 - }; - - mergedChildren - } - } -} diff --git a/.mops/core@2.5.0/src/Nat.mo b/.mops/core@2.5.0/src/Nat.mo deleted file mode 100644 index e93f58a..0000000 --- a/.mops/core@2.5.0/src/Nat.mo +++ /dev/null @@ -1,671 +0,0 @@ -/// Natural numbers with infinite precision. -/// -/// Most operations on natural numbers (e.g. addition) are available as built-in operators (e.g. `1 + 1`). -/// This module provides equivalent functions and `Text` conversion. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat "mo:core/Nat"; -/// ``` - -import Int "Int"; -import Prim "mo:⛔"; -import Char "Char"; -import Iter "Iter"; -import Runtime "Runtime"; -import Order "Order"; - -module { - - /// Infinite precision natural numbers. - public type Nat = Prim.Types.Nat; - - /// Converts a natural number to its textual representation. Textual - /// representation _do not_ contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toText(1234) == "1234"; - /// ``` - public let toText : (self : Nat) -> Text = Int.toText; - - /// Creates a natural number from its textual representation. Returns `null` - /// if the input is not a valid natural number. - /// - /// The textual representation _must not_ contain underscores. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromText("1234") == ?1234; - /// ``` - public func fromText(text : Text) : ?Nat { - if (text == "") { - return null - }; - var n = 0; - for (c in text.chars()) { - if (Char.isDigit(c)) { - let charAsNat = Prim.nat32ToNat(Prim.charToNat32(c) -% Prim.charToNat32('0')); - n := n * 10 + charAsNat - } else { - return null - } - }; - ?n - }; - - /// Creates a natural number from its textual representation. Returns `null` - /// if the input is not a valid natural number. - /// - /// The textual representation _must not_ contain underscores. - /// - /// This functions is meant to be used with contextual-dot notation. - /// - /// Example: - /// ```motoko include=import - /// assert "1234".toNat() == ?1234; - /// ``` - public let toNat : (self : Text) -> ?Nat = fromText; - - /// Converts an integer to a natural number. Traps if the integer is negative. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromInt(1234) == (1234 : Nat); - /// ``` - /// @deprecated M0235 - public func fromInt(int : Int) : Nat { - if (int < 0) { - Runtime.trap("Nat.fromInt(): negative input value") - } else { - Int.abs(int) - } - }; - - /// Conversion to Float. May result in `Inf`. - /// - /// Note: The floating point number may be imprecise for large Nat values. - /// Returns `inf` if the integer is greater than the maximum floating point number. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toFloat(123) == 123.0; - /// ``` - public let toFloat : (self : Nat) -> Float = Int.toFloat; - - /// Converts a natural number to an integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toInt(1234) == 1234; - /// ``` - public func toInt(self : Nat) : Int { - self : Int - }; - - /// Converts an unsigned integer with infinite precision to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toNat8(123) == (123 : Nat8); - /// ``` - public let toNat8 : (self : Nat) -> Nat8 = Prim.natToNat8; - - /// Converts an unsigned integer with infinite precision to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toNat16(123) == (123 : Nat16); - /// ``` - public let toNat16 : (self : Nat) -> Nat16 = Prim.natToNat16; - - /// Converts an unsigned integer with infinite precision to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toNat32(123) == (123 : Nat32); - /// ``` - public let toNat32 : (self : Nat) -> Nat32 = Prim.natToNat32; - - /// Converts an unsigned integer with infinite precision to a 64-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.toNat64(123) == (123 : Nat64); - /// ``` - public let toNat64 : (self : Nat) -> Nat64 = Prim.natToNat64; - - /// Converts an 8-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromNat8(123) == (123 : Nat); - /// ``` - public let fromNat8 : Nat8 -> Nat = Prim.nat8ToNat; - - /// Converts a 16-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromNat16(123) == (123 : Nat); - /// ``` - public let fromNat16 : Nat16 -> Nat = Prim.nat16ToNat; - - /// Converts a 32-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromNat32(123) == (123 : Nat); - /// ``` - public let fromNat32 : Nat32 -> Nat = Prim.nat32ToNat; - - /// Converts a 64-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.fromNat64(123) == (123 : Nat); - /// ``` - public let fromNat64 : Nat64 -> Nat = Prim.nat64ToNat; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.min(1, 2) == 1; - /// ``` - public func min(x : Nat, y : Nat) : Nat { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.max(1, 2) == 2; - /// ``` - public func max(x : Nat, y : Nat) : Nat { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.equal(1, 1); - /// assert 1 == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a = 111; - /// let b = 222; - /// assert not Nat.equal(a, b); - /// ``` - public func equal(x : Nat, y : Nat) : Bool { x == y }; - - /// Inequality function for Nat types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.notEqual(1, 2); - /// assert 1 != 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat, y : Nat) : Bool { x != y }; - - /// "Less than" function for Nat types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.less(1, 2); - /// assert 1 < 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat, y : Nat) : Bool { x < y }; - - /// "Less than or equal" function for Nat types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.lessOrEqual(1, 2); - /// assert 1 <= 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat, y : Nat) : Bool { x <= y }; - - /// "Greater than" function for Nat types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.greater(2, 1); - /// assert 2 > 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat, y : Nat) : Bool { x > y }; - - /// "Greater than or equal" function for Nat types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.greaterOrEqual(2, 1); - /// assert 2 >= 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat, y : Nat) : Bool { x >= y }; - - /// General purpose comparison function for `Nat`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1], Nat.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat, y : Nat) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. This operator will never overflow - /// because `Nat` is infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.add(1, 2) == 3; - /// assert 1 + 2 == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat.add) == 6; - /// ``` - public func add(x : Nat, y : Nat) : Nat { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow below `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.sub(2, 1) == 1; - /// // Add a type annotation to avoid a warning about the subtraction - /// assert 2 - 1 : Nat == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 10, Nat.sub) == 4; - /// ``` - public func sub(x : Nat, y : Nat) : Nat { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. This operator will never - /// overflow because `Nat` is infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.mul(2, 3) == 6; - /// assert 2 * 3 == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat.mul) == 6; - /// ``` - public func mul(x : Nat, y : Nat) : Nat { x * y }; - - /// Returns the unsigned integer division of `x` by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// The quotient is rounded down, which is equivalent to truncating the - /// decimal places of the quotient. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.div(6, 2) == 3; - /// assert 6 / 2 == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat, y : Nat) : Nat { x / y }; - - /// Returns the remainder of unsigned integer division of `x` by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.rem(6, 4) == 2; - /// assert 6 % 4 == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat, y : Nat) : Nat { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. Traps when `y > 2^32`. This operator - /// will never overflow because `Nat` is infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.pow(2, 3) == 8; - /// assert 2 ** 3 == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat, y : Nat) : Nat { x ** y }; - - /// Returns the (conceptual) bitwise shift left of `x` by `y`, `x * (2 ** y)`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.bitshiftLeft(1, 3) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in absence - /// of the `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. While `Nat` is not defined in terms - /// of bit patterns, conceptually it can be regarded as such, and the operation - /// is provided as a high-performance version of the corresponding arithmetic - /// rule. - public let bitshiftLeft : (x : Nat, y : Nat32) -> Nat = Prim.shiftLeft; - - /// Returns the (conceptual) bitwise shift right of `x` by `y`, `x / (2 ** y)`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat.bitshiftRight(8, 3) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in absence - /// of the `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. While `Nat` is not defined in terms - /// of bit patterns, conceptually it can be regarded as such, and the operation - /// is provided as a high-performance version of the corresponding arithmetic - /// rule. - public let bitshiftRight : (x : Nat, y : Nat32) -> Nat = Prim.shiftRight; - - /// Returns an iterator over `Nat` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat, toExclusive : Nat) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat { - if (n >= toExclusive) { - return null - }; - let current = n; - n += 1; - ?current - } - } - } - }; - - /// Returns an iterator over `Nat` values from the first to second argument with an exclusive upper bound, - /// incrementing by the specified step size. The step can be positive or negative. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// // Positive step - /// let iter1 = Nat.rangeBy(1, 7, 2); - /// assert iter1.next() == ?1; - /// assert iter1.next() == ?3; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// // Negative step - /// let iter2 = Nat.rangeBy(7, 1, -2); - /// assert iter2.next() == ?7; - /// assert iter2.next() == ?5; - /// assert iter2.next() == ?3; - /// assert iter2.next() == null; - /// ``` - /// - /// If `step` is 0 or if the iteration would not progress towards the bound, returns an empty iterator. - public func rangeBy(fromInclusive : Nat, toExclusive : Nat, step : Int) : Iter.Iter { - if (step == 0 or (step > 0 and fromInclusive >= toExclusive) or (step < 0 and fromInclusive <= toExclusive)) { - Iter.empty() - } else if (step > 0) { - object { - let stepMagnitude = Int.abs(step); - var n = fromInclusive; - public func next() : ?Nat { - if (n >= toExclusive) { - return null - }; - let current = n; - n += stepMagnitude; - ?current - } - } - } else { - object { - let stepMagnitude = Int.abs(step); - var n = fromInclusive; - public func next() : ?Nat { - if (n <= toExclusive) { - return null - }; - let current = n; - if (stepMagnitude > n) { - n := 0 - } else { - n -= stepMagnitude - }; - ?current - } - } - } - }; - - /// Returns an iterator over the integers from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.rangeInclusive(3, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat, to : Nat) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - public func next() : ?Nat { - if (n > to) { - return null - }; - let current = n; - n += 1; - ?current - } - } - } - }; - - /// Returns an iterator over the integers from the first to second argument, inclusive, - /// incrementing by the specified step size. The step can be positive or negative. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// // Positive step - /// let iter1 = Nat.rangeByInclusive(1, 7, 2); - /// assert iter1.next() == ?1; - /// assert iter1.next() == ?3; - /// assert iter1.next() == ?5; - /// assert iter1.next() == ?7; - /// assert iter1.next() == null; - /// - /// // Negative step - /// let iter2 = Nat.rangeByInclusive(7, 1, -2); - /// assert iter2.next() == ?7; - /// assert iter2.next() == ?5; - /// assert iter2.next() == ?3; - /// assert iter2.next() == ?1; - /// assert iter2.next() == null; - /// ``` - /// - /// If `from == to`, return an iterator which only returns that value. - /// - /// Otherwise, if `step` is 0 or if the iteration would not progress towards the bound, returns an empty iterator. - public func rangeByInclusive(from : Nat, to : Nat, step : Int) : Iter.Iter { - if (from == to) { - Iter.singleton(from) - } else if (step == 0 or (step > 0 and from > to) or (step < 0 and from < to)) { - Iter.empty() - } else if (step > 0) { - object { - let stepMagnitude = Int.abs(step); - var n = from; - public func next() : ?Nat { - if (n > to) { - return null - }; - let current = n; - n += stepMagnitude; - ?current - } - } - } else { - object { - let stepMagnitude = Int.abs(step); - var n = from; - var done = false; - public func next() : ?Nat { - if (done) { - null - } else { - let current = n; - if (n < to + stepMagnitude) { - done := true - } else { - n -= stepMagnitude - }; - ?current - } - } - } - } - }; - - /// Returns an infinite iterator over all possible `Nat` values. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter = object { - var n = 0; - public func next() : ?Nat { - let current = n; - n += 1; - ?current - } - }; - -} diff --git a/.mops/core@2.5.0/src/Nat16.mo b/.mops/core@2.5.0/src/Nat16.mo deleted file mode 100644 index 4b1195d..0000000 --- a/.mops/core@2.5.0/src/Nat16.mo +++ /dev/null @@ -1,705 +0,0 @@ -/// Utility functions on 16-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat16 "mo:core/Nat16"; -/// ``` -import Nat "Nat"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 16-bit natural numbers. - public type Nat16 = Prim.Types.Nat16; - - /// Maximum 16-bit natural number. `2 ** 16 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.maxValue == (65535 : Nat16); - /// ``` - public let maxValue : Nat16 = 65535; - - /// Converts a 16-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toNat(123) == (123 : Nat); - /// ``` - public let toNat : (self : Nat16) -> Nat = Prim.nat16ToNat; - - /// Converts an unsigned integer with infinite precision to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromNat(123) == (123 : Nat16); - /// ``` - public let fromNat : Nat -> Nat16 = Prim.natToNat16; - - /// Converts an 8-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromNat8(123) == (123 : Nat16); - /// ``` - /// @deprecated M0235 - public let fromNat8 : (x : Nat8) -> Nat16 = Prim.nat8ToNat16; - - /// Converts a 16-bit unsigned integer to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toNat8(123) == (123 : Nat8); - /// ``` - public let toNat8 : (self : Nat16) -> Nat8 = Prim.nat16ToNat8; - - /// Converts a 32-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromNat32(123) == (123 : Nat16); - /// ``` - /// @deprecated M0235 - public let fromNat32 : (x : Nat32) -> Nat16 = Prim.nat32ToNat16; - - /// Converts a 16-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toNat32(123) == (123 : Nat32); - /// ``` - public let toNat32 : (self : Nat16) -> Nat32 = Prim.nat16ToNat32; - - /// Converts a 64-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromNat64(123) == (123 : Nat16); - /// ``` - /// @deprecated M0235 - public func fromNat64(x : Nat64) : Nat16 { - Prim.nat32ToNat16(Prim.nat64ToNat32(x)) - }; - - /// Converts a 16-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toNat64(123) == (123 : Nat64); - /// ``` - public func toNat64(self : Nat16) : Nat64 { - Prim.nat32ToNat64(Prim.nat16ToNat32(self)) - }; - - /// Converts a signed integer with infinite precision to a 16-bit unsigned integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.fromIntWrap(123 : Int) == (123 : Nat16); - /// ``` - public let fromIntWrap : Int -> Nat16 = Prim.intToNat16Wrap; - - /// Converts `x` to its textual representation. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.toText(1234) == ("1234" : Text); - /// ``` - public func toText(self : Nat16) : Text { - Nat.toText(toNat(self)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.min(123, 200) == (123 : Nat16); - /// ``` - public func min(x : Nat16, y : Nat16) : Nat16 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.max(123, 200) == (200 : Nat16); - /// ``` - public func max(x : Nat16, y : Nat16) : Nat16 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat16 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.equal(1, 1); - /// assert (1 : Nat16) == (1 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Nat16 = 111; - /// let b : Nat16 = 222; - /// assert not Nat16.equal(a, b); - /// ``` - public func equal(x : Nat16, y : Nat16) : Bool { x == y }; - - /// Inequality function for Nat16 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.notEqual(1, 2); - /// assert (1 : Nat16) != (2 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat16, y : Nat16) : Bool { x != y }; - - /// "Less than" function for Nat16 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.less(1, 2); - /// assert (1 : Nat16) < (2 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat16, y : Nat16) : Bool { x < y }; - - /// "Less than or equal" function for Nat16 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.lessOrEqual(1, 2); - /// assert (1 : Nat16) <= (2 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat16, y : Nat16) : Bool { x <= y }; - - /// "Greater than" function for Nat16 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.greater(2, 1); - /// assert (2 : Nat16) > (1 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat16, y : Nat16) : Bool { x > y }; - - /// "Greater than or equal" function for Nat16 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.greaterOrEqual(2, 1); - /// assert (2 : Nat16) >= (1 : Nat16); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat16, y : Nat16) : Bool { - x >= y - }; - - /// General purpose comparison function for `Nat16`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1] : [Nat16], Nat16.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat16, y : Nat16) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.add(1, 2) == 3; - /// assert (1 : Nat16) + (2 : Nat16) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat16.add) == 6; - /// ``` - public func add(x : Nat16, y : Nat16) : Nat16 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.sub(2, 1) == 1; - /// assert (2 : Nat16) - (1 : Nat16) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 20, Nat16.sub) == 14; - /// ``` - public func sub(x : Nat16, y : Nat16) : Nat16 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.mul(2, 3) == 6; - /// assert (2 : Nat16) * (3 : Nat16) == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat16.mul) == 6; - /// ``` - public func mul(x : Nat16, y : Nat16) : Nat16 { x * y }; - - /// Returns the quotient of `x` divided by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.div(6, 2) == 3; - /// assert (6 : Nat16) / (2 : Nat16) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat16, y : Nat16) : Nat16 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.rem(6, 4) == 2; - /// assert (6 : Nat16) % (4 : Nat16) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat16, y : Nat16) : Nat16 { x % y }; - - /// Returns the power of `x` to `y`, `x ** y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.pow(2, 3) == 8; - /// assert (2 : Nat16) ** (3 : Nat16) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat16, y : Nat16) : Nat16 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitnot(0) == 65535; - /// assert ^(0 : Nat16) == 65535; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat16) : Nat16 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitand(0, 1) == 0; - /// assert (0 : Nat16) & (1 : Nat16) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat16, y : Nat16) : Nat16 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitor(0, 1) == 1; - /// assert (0 : Nat16) | (1 : Nat16) == 1; - /// ``` - public func bitor(x : Nat16, y : Nat16) : Nat16 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitxor(0, 1) == 1; - /// assert (0 : Nat16) ^ (1 : Nat16) == 1; - /// ``` - public func bitxor(x : Nat16, y : Nat16) : Nat16 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitshiftLeft(1, 3) == 8; - /// assert (1 : Nat16) << (3 : Nat16) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat16, y : Nat16) : Nat16 { - x << y - }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitshiftRight(8, 3) == 1; - /// assert (8 : Nat16) >> (3 : Nat16) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat16, y : Nat16) : Nat16 { - x >> y - }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitrotLeft(2, 1) == 4; - /// assert (2 : Nat16) <<> (1 : Nat16) == 4; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat16, y : Nat16) : Nat16 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitrotRight(1, 1) == 32768; - /// assert (1 : Nat16) <>> (1 : Nat16) == 32768; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat16, y : Nat16) : Nat16 { - x <>> y - }; - - /// Returns the value of bit `p mod 16` in `x`, `(x & 2^(p mod 16)) == 2^(p mod 16)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bittest(5, 2); - /// ``` - public func bittest(x : Nat16, p : Nat) : Bool { - Prim.btstNat16(x, Prim.natToNat16(p)) - }; - - /// Returns the value of setting bit `p mod 16` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitset(0, 2) == 4; - /// ``` - public func bitset(x : Nat16, p : Nat) : Nat16 { - x | (1 << Prim.natToNat16(p)) - }; - - /// Returns the value of clearing bit `p mod 16` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitclear(5, 2) == 1; - /// ``` - public func bitclear(x : Nat16, p : Nat) : Nat16 { - x & ^(1 << Prim.natToNat16(p)) - }; - - /// Returns the value of flipping bit `p mod 16` in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitflip(5, 2) == 1; - /// ``` - public func bitflip(x : Nat16, p : Nat) : Nat16 { - x ^ (1 << Prim.natToNat16(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitcountNonZero(5) == 2; - /// ``` - public let bitcountNonZero : (x : Nat16) -> Nat16 = Prim.popcntNat16; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitcountLeadingZero(5) == 13; - /// ``` - public let bitcountLeadingZero : (x : Nat16) -> Nat16 = Prim.clzNat16; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.bitcountTrailingZero(5) == 0; - /// ``` - public let bitcountTrailingZero : (x : Nat16) -> Nat16 = Prim.ctzNat16; - - /// Returns the upper (i.e. most significant) and lower (least significant) byte of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.explode 0xaa88 == (170, 136); - /// ``` - public let explode : (x : Nat16) -> (msb : Nat8, lsb : Nat8) = Prim.explodeNat16; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.addWrap(65532, 5) == 1; - /// assert (65532 : Nat16) +% (5 : Nat16) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat16, y : Nat16) : Nat16 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.subWrap(1, 2) == 65535; - /// assert (1 : Nat16) -% (2 : Nat16) == 65535; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat16, y : Nat16) : Nat16 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.mulWrap(655, 101) == 619; - /// assert (655 : Nat16) *% (101 : Nat16) == 619; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat16, y : Nat16) : Nat16 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat16.powWrap(2, 16) == 0; - /// assert (2 : Nat16) **% (16 : Nat16) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat16, y : Nat16) : Nat16 { x **% y }; - - /// Returns an iterator over `Nat16` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat16, toExclusive : Nat16) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat16 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Nat16` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat16, to : Nat16) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Nat16 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Nat16 values, from 0 to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat16.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(0, maxValue) - }; - -} diff --git a/.mops/core@2.5.0/src/Nat32.mo b/.mops/core@2.5.0/src/Nat32.mo deleted file mode 100644 index f4759f1..0000000 --- a/.mops/core@2.5.0/src/Nat32.mo +++ /dev/null @@ -1,724 +0,0 @@ -/// Utility functions on 32-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat32 "mo:core/Nat32"; -/// ``` -import Nat "Nat"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 32-bit natural numbers. - public type Nat32 = Prim.Types.Nat32; - - /// Maximum 32-bit natural number. `2 ** 32 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.maxValue == (4294967295 : Nat32); - /// ``` - public let maxValue : Nat32 = 4294967295; - - /// Converts a 32-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toNat(123) == (123 : Nat); - /// ``` - public let toNat : (self : Nat32) -> Nat = Prim.nat32ToNat; - - /// Converts an unsigned integer with infinite precision to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromNat(123) == (123 : Nat32); - /// ``` - public let fromNat : Nat -> Nat32 = Prim.natToNat32; - - /// Converts a 32-bit unsigned integer to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toNat8(123) == (123 : Nat8); - /// ``` - public func toNat8(self : Nat32) : Nat8 { - Prim.nat16ToNat8(Prim.nat32ToNat16(self)) - }; - - /// Converts an 8-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromNat8(123) == (123 : Nat32); - /// ``` - /// @deprecated M0235 - public func fromNat8(x : Nat8) : Nat32 { - Prim.nat16ToNat32(Prim.nat8ToNat16(x)) - }; - - /// Converts a 16-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromNat16(123) == (123 : Nat32); - /// ``` - /// @deprecated M0235 - public let fromNat16 : (x : Nat16) -> Nat32 = Prim.nat16ToNat32; - - /// Converts a 32-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toNat16(123) == (123 : Nat16); - /// ``` - public let toNat16 : (self : Nat32) -> Nat16 = Prim.nat32ToNat16; - - /// Converts a 64-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromNat64(123) == (123 : Nat32); - /// ``` - /// @deprecated M0235 - public let fromNat64 : (x : Nat64) -> Nat32 = Prim.nat64ToNat32; - - /// Converts a 32-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toNat64(123) == (123 : Nat64); - /// ``` - public let toNat64 : (self : Nat32) -> Nat64 = Prim.nat32ToNat64; - - /// Converts a signed integer with infinite precision to a 32-bit unsigned integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.fromIntWrap(123) == (123 : Nat32); - /// ``` - public let fromIntWrap : Int -> Nat32 = Prim.intToNat32Wrap; - - /// Convert a Nat32 `char` to a Char in its Unicode representation. - /// - /// Example: - /// ```motoko include=import - /// let unicode = Nat32.toChar(65); - /// assert unicode == 'A'; - /// ``` - public let toChar : (self : Nat32) -> Char = Prim.nat32ToChar; - - /// Converts `x` to its textual representation. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.toText(1234) == ("1234" : Text); - /// ``` - public func toText(self : Nat32) : Text { - Nat.toText(toNat(self)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.min(123, 456) == (123 : Nat32); - /// ``` - public func min(x : Nat32, y : Nat32) : Nat32 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.max(123, 456) == (456 : Nat32); - /// ``` - public func max(x : Nat32, y : Nat32) : Nat32 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat32 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.equal(1, 1); - /// assert (1 : Nat32) == (1 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Nat32 = 111; - /// let b : Nat32 = 222; - /// assert not Nat32.equal(a, b); - /// ``` - public func equal(x : Nat32, y : Nat32) : Bool { x == y }; - - /// Inequality function for Nat32 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.notEqual(1, 2); - /// assert (1 : Nat32) != (2 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat32, y : Nat32) : Bool { x != y }; - - /// "Less than" function for Nat32 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.less(1, 2); - /// assert (1 : Nat32) < (2 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat32, y : Nat32) : Bool { x < y }; - - /// "Less than or equal" function for Nat32 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.lessOrEqual(1, 2); - /// assert (1 : Nat32) <= (2 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat32, y : Nat32) : Bool { x <= y }; - - /// "Greater than" function for Nat32 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.greater(2, 1); - /// assert (2 : Nat32) > (1 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat32, y : Nat32) : Bool { x > y }; - - /// "Greater than or equal" function for Nat32 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.greaterOrEqual(2, 1); - /// assert (2 : Nat32) >= (1 : Nat32); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat32, y : Nat32) : Bool { - x >= y - }; - - /// General purpose comparison function for `Nat32`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1] : [Nat32], Nat32.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat32, y : Nat32) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.add(1, 2) == 3; - /// assert (1 : Nat32) + (2 : Nat32) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat32.add) == 6; - /// ``` - public func add(x : Nat32, y : Nat32) : Nat32 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.sub(2, 1) == 1; - /// assert (2 : Nat32) - (1 : Nat32) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 20, Nat32.sub) == 14; - /// ``` - public func sub(x : Nat32, y : Nat32) : Nat32 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.mul(2, 3) == 6; - /// assert (2 : Nat32) * (3 : Nat32) == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat32.mul) == 6; - /// ``` - public func mul(x : Nat32, y : Nat32) : Nat32 { x * y }; - - /// Returns the division of `x by y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.div(6, 2) == 3; - /// assert (6 : Nat32) / (2 : Nat32) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat32, y : Nat32) : Nat32 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.rem(6, 4) == 2; - /// assert (6 : Nat32) % (4 : Nat32) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat32, y : Nat32) : Nat32 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.pow(2, 3) == 8; - /// assert (2 : Nat32) ** (3 : Nat32) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat32, y : Nat32) : Nat32 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitnot(0) == 4294967295; - /// assert ^(0 : Nat32) == 4294967295; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat32) : Nat32 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitand(1, 3) == 1; - /// assert (1 : Nat32) & (3 : Nat32) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat32, y : Nat32) : Nat32 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitor(1, 3) == 3; - /// assert (1 : Nat32) | (3 : Nat32) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Nat32, y : Nat32) : Nat32 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitxor(1, 3) == 2; - /// assert (1 : Nat32) ^ (3 : Nat32) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Nat32, y : Nat32) : Nat32 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitshiftLeft(1, 3) == 8; - /// assert (1 : Nat32) << (3 : Nat32) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat32, y : Nat32) : Nat32 { - x << y - }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitshiftRight(8, 3) == 1; - /// assert (8 : Nat32) >> (3 : Nat32) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat32, y : Nat32) : Nat32 { - x >> y - }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitrotLeft(1, 3) == 8; - /// assert (1 : Nat32) <<> (3 : Nat32) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat32, y : Nat32) : Nat32 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitrotRight(1, 1) == 2147483648; - /// assert (1 : Nat32) <>> (1 : Nat32) == 2147483648; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat32, y : Nat32) : Nat32 { - x <>> y - }; - - /// Returns the value of bit `p mod 32` in `x`, `(x & 2^(p mod 32)) == 2^(p mod 32)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bittest(5, 2); - /// ``` - public func bittest(x : Nat32, p : Nat) : Bool { - Prim.btstNat32(x, Prim.natToNat32(p)) - }; - - /// Returns the value of setting bit `p mod 32` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitset(5, 1) == 7; - /// ``` - public func bitset(x : Nat32, p : Nat) : Nat32 { - x | (1 << Prim.natToNat32(p)) - }; - - /// Returns the value of clearing bit `p mod 32` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitclear(5, 2) == 1; - /// ``` - public func bitclear(x : Nat32, p : Nat) : Nat32 { - x & ^(1 << Prim.natToNat32(p)) - }; - - /// Returns the value of flipping bit `p mod 32` in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitflip(5, 2) == 1; - /// ``` - public func bitflip(x : Nat32, p : Nat) : Nat32 { - x ^ (1 << Prim.natToNat32(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitcountNonZero(5) == 2; - /// ``` - public let bitcountNonZero : (x : Nat32) -> Nat32 = Prim.popcntNat32; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitcountLeadingZero(5) == 29; - /// ``` - public let bitcountLeadingZero : (x : Nat32) -> Nat32 = Prim.clzNat32; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.bitcountTrailingZero(16) == 4; - /// ``` - public let bitcountTrailingZero : (x : Nat32) -> Nat32 = Prim.ctzNat32; - - /// Returns the upper (i.e. most significant), lower (least significant) - /// and in-between bytes of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.explode 0xaa885511 == (170, 136, 85, 17); - /// ``` - public let explode : (x : Nat32) -> (msb : Nat8, Nat8, Nat8, lsb : Nat8) = Prim.explodeNat32; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.addWrap(4294967295, 1) == 0; - /// assert (4294967295 : Nat32) +% (1 : Nat32) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat32, y : Nat32) : Nat32 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.subWrap(0, 1) == 4294967295; - /// assert (0 : Nat32) -% (1 : Nat32) == 4294967295; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat32, y : Nat32) : Nat32 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.mulWrap(2147483648, 2) == 0; - /// assert (2147483648 : Nat32) *% (2 : Nat32) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat32, y : Nat32) : Nat32 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat32.powWrap(2, 32) == 0; - /// assert (2 : Nat32) **% (32 : Nat32) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat32, y : Nat32) : Nat32 { x **% y }; - - /// Returns an iterator over `Nat32` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat32, toExclusive : Nat32) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat32 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Nat32` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat32, to : Nat32) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Nat32 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Nat32 values, from 0 to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat32.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(0, maxValue) - }; - -} diff --git a/.mops/core@2.5.0/src/Nat64.mo b/.mops/core@2.5.0/src/Nat64.mo deleted file mode 100644 index e16a9f1..0000000 --- a/.mops/core@2.5.0/src/Nat64.mo +++ /dev/null @@ -1,719 +0,0 @@ -/// Utility functions on 64-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat64 "mo:core/Nat64"; -/// ``` -import Nat "Nat"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 64-bit natural numbers. - public type Nat64 = Prim.Types.Nat64; - - /// Maximum 64-bit natural number. `2 ** 64 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.maxValue == (18446744073709551615 : Nat64); - /// ``` - public let maxValue : Nat64 = 18446744073709551615; - - /// Converts a 64-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toNat(123) == (123 : Nat); - /// ``` - public let toNat : (self : Nat64) -> Nat = Prim.nat64ToNat; - - /// Converts an unsigned integer with infinite precision to a 64-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromNat(123) == (123 : Nat64); - /// ``` - public let fromNat : Nat -> Nat64 = Prim.natToNat64; - - /// Converts a 64-bit unsigned integer to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toNat8(123) == (123 : Nat8); - /// ``` - public func toNat8(self : Nat64) : Nat8 { - Prim.nat16ToNat8(Prim.nat32ToNat16(Prim.nat64ToNat32(self))) - }; - - /// Converts a 16-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromNat16(123) == (123 : Nat64); - /// ``` - /// @deprecated M0235 - public func fromNat16(x : Nat16) : Nat64 { - Prim.nat32ToNat64(Prim.nat16ToNat32(x)) - }; - - /// Converts a 64-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toNat16(123) == (123 : Nat16); - /// ``` - public func toNat16(self : Nat64) : Nat16 { - Prim.nat32ToNat16(Prim.nat64ToNat32(self)) - }; - - /// Converts an 8-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromNat8(123) == (123 : Nat64); - /// ``` - /// @deprecated M0235 - public func fromNat8(x : Nat8) : Nat64 { - Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(x))) - }; - - /// Converts a 32-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromNat32(123) == (123 : Nat64); - /// ``` - /// @deprecated M0235 - public let fromNat32 : (x : Nat32) -> Nat64 = Prim.nat32ToNat64; - - /// Converts a 64-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toNat32(123) == (123 : Nat32); - /// ``` - public let toNat32 : (self : Nat64) -> Nat32 = Prim.nat64ToNat32; - - /// Converts a signed integer with infinite precision to a 64-bit unsigned integer. - /// - /// Traps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.fromIntWrap(123) == (123 : Nat64); - /// ``` - public let fromIntWrap : Int -> Nat64 = Prim.intToNat64Wrap; - - /// Converts `x` to its textual representation. Textual representation _do not_ - /// contain underscores to represent commas. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.toText(1234) == ("1234" : Text); - /// ``` - public func toText(self : Nat64) : Text { - Nat.toText(toNat(self)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.min(123, 456) == (123 : Nat64); - /// ``` - public func min(x : Nat64, y : Nat64) : Nat64 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.max(123, 456) == (456 : Nat64); - /// ``` - public func max(x : Nat64, y : Nat64) : Nat64 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat64 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.equal(1, 1); - /// assert (1 : Nat64) == (1 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Nat64 = 111; - /// let b : Nat64 = 222; - /// assert not Nat64.equal(a, b); - /// ``` - public func equal(x : Nat64, y : Nat64) : Bool { x == y }; - - /// Inequality function for Nat64 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.notEqual(1, 2); - /// assert (1 : Nat64) != (2 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat64, y : Nat64) : Bool { x != y }; - - /// "Less than" function for Nat64 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.less(1, 2); - /// assert (1 : Nat64) < (2 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat64, y : Nat64) : Bool { x < y }; - - /// "Less than or equal" function for Nat64 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.lessOrEqual(1, 2); - /// assert (1 : Nat64) <= (2 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat64, y : Nat64) : Bool { x <= y }; - - /// "Greater than" function for Nat64 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.greater(2, 1); - /// assert (2 : Nat64) > (1 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat64, y : Nat64) : Bool { x > y }; - - /// "Greater than or equal" function for Nat64 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.greaterOrEqual(2, 1); - /// assert (2 : Nat64) >= (1 : Nat64); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat64, y : Nat64) : Bool { - x >= y - }; - - /// General purpose comparison function for `Nat64`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1] : [Nat64], Nat64.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat64, y : Nat64) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.add(1, 2) == 3; - /// assert (1 : Nat64) + (2 : Nat64) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat64.add) == 6; - /// ``` - public func add(x : Nat64, y : Nat64) : Nat64 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.sub(3, 1) == 2; - /// assert (3 : Nat64) - (1 : Nat64) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 10, Nat64.sub) == 4; - /// ``` - public func sub(x : Nat64, y : Nat64) : Nat64 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.mul(2, 3) == 6; - /// assert (2 : Nat64) * (3 : Nat64) == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat64.mul) == 6; - /// ``` - public func mul(x : Nat64, y : Nat64) : Nat64 { x * y }; - - /// Returns the quotient of `x` divided by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.div(6, 2) == 3; - /// assert (6 : Nat64) / (2 : Nat64) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat64, y : Nat64) : Nat64 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.rem(6, 4) == 2; - /// assert (6 : Nat64) % (4 : Nat64) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat64, y : Nat64) : Nat64 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.pow(2, 3) == 8; - /// assert (2 : Nat64) ** (3 : Nat64) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat64, y : Nat64) : Nat64 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitnot(0) == 18446744073709551615; - /// assert ^(0 : Nat64) == 18446744073709551615; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat64) : Nat64 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitand(1, 3) == 1; - /// assert (1 : Nat64) & (3 : Nat64) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat64, y : Nat64) : Nat64 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitor(1, 3) == 3; - /// assert (1 : Nat64) | (3 : Nat64) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Nat64, y : Nat64) : Nat64 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitxor(1, 3) == 2; - /// assert (1 : Nat64) ^ (3 : Nat64) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Nat64, y : Nat64) : Nat64 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitshiftLeft(1, 3) == 8; - /// assert (1 : Nat64) << (3 : Nat64) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat64, y : Nat64) : Nat64 { - x << y - }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitshiftRight(8, 3) == 1; - /// assert (8 : Nat64) >> (3 : Nat64) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat64, y : Nat64) : Nat64 { - x >> y - }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitrotLeft(1, 3) == 8; - /// assert (1 : Nat64) <<> (3 : Nat64) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat64, y : Nat64) : Nat64 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitrotRight(8, 3) == 1; - /// assert (8 : Nat64) <>> (3 : Nat64) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat64, y : Nat64) : Nat64 { - x <>> y - }; - - /// Returns the value of bit `p mod 64` in `x`, `(x & 2^(p mod 64)) == 2^(p mod 64)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bittest(5, 2); - /// ``` - public func bittest(x : Nat64, p : Nat) : Bool { - Prim.btstNat64(x, Prim.natToNat64(p)) - }; - - /// Returns the value of setting bit `p mod 64` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitset(5, 1) == 7; - /// ``` - public func bitset(x : Nat64, p : Nat) : Nat64 { - x | (1 << Prim.natToNat64(p)) - }; - - /// Returns the value of clearing bit `p mod 64` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitclear(5, 2) == 1; - /// ``` - public func bitclear(x : Nat64, p : Nat) : Nat64 { - x & ^(1 << Prim.natToNat64(p)) - }; - - /// Returns the value of flipping bit `p mod 64` in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitflip(5, 2) == 1; - /// ``` - public func bitflip(x : Nat64, p : Nat) : Nat64 { - x ^ (1 << Prim.natToNat64(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitcountNonZero(5) == 2; - /// ``` - public let bitcountNonZero : (x : Nat64) -> Nat64 = Prim.popcntNat64; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitcountLeadingZero(5) == 61; - /// ``` - public let bitcountLeadingZero : (x : Nat64) -> Nat64 = Prim.clzNat64; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.bitcountTrailingZero(16) == 4; - /// ``` - public let bitcountTrailingZero : (x : Nat64) -> Nat64 = Prim.ctzNat64; - - /// Returns the upper (i.e. most significant), lower (least significant) - /// and in-between bytes of `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.explode 0xbb772266aa885511 == (187, 119, 34, 102, 170, 136, 85, 17); - /// ``` - public let explode : (x : Nat64) -> (msb : Nat8, Nat8, Nat8, Nat8, Nat8, Nat8, Nat8, lsb : Nat8) = Prim.explodeNat64; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.addWrap(Nat64.maxValue, 1) == 0; - /// assert Nat64.maxValue +% (1 : Nat64) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat64, y : Nat64) : Nat64 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.subWrap(0, 1) == 18446744073709551615; - /// assert (0 : Nat64) -% (1 : Nat64) == 18446744073709551615; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat64, y : Nat64) : Nat64 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.mulWrap(4294967296, 4294967296) == 0; - /// assert (4294967296 : Nat64) *% (4294967296 : Nat64) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat64, y : Nat64) : Nat64 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat64.powWrap(2, 64) == 0; - /// assert (2 : Nat64) **% (64 : Nat64) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat64, y : Nat64) : Nat64 { x **% y }; - - /// Returns an iterator over `Nat64` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat64, toExclusive : Nat64) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat64 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Nat64` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat64, to : Nat64) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Nat64 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Nat64 values, from 0 to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat64.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(0, maxValue) - }; - -} diff --git a/.mops/core@2.5.0/src/Nat8.mo b/.mops/core@2.5.0/src/Nat8.mo deleted file mode 100644 index 429aa7e..0000000 --- a/.mops/core@2.5.0/src/Nat8.mo +++ /dev/null @@ -1,698 +0,0 @@ -/// Utility functions on 8-bit unsigned integers. -/// -/// Note that most operations are available as built-in operators (e.g. `1 + 1`). -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Nat8 "mo:core/Nat8"; -/// ``` -import Nat "Nat"; -import Iter "Iter"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// 8-bit natural numbers. - public type Nat8 = Prim.Types.Nat8; - - /// Maximum 8-bit natural number. `2 ** 8 - 1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.maxValue == (255 : Nat8); - /// ``` - public let maxValue : Nat8 = 255; - - /// Converts an 8-bit unsigned integer to an unsigned integer with infinite precision. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toNat(123) == (123 : Nat); - /// ``` - public let toNat : (self : Nat8) -> Nat = Prim.nat8ToNat; - - /// Converts an unsigned integer with infinite precision to an 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromNat(123) == (123 : Nat8); - /// ``` - public let fromNat : Nat -> Nat8 = Prim.natToNat8; - - /// Converts a 16-bit unsigned integer to a 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromNat16(123) == (123 : Nat8); - /// ``` - public let fromNat16 : Nat16 -> Nat8 = Prim.nat16ToNat8; - - /// Converts an 8-bit unsigned integer to a 16-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toNat16(123) == (123 : Nat16); - /// ``` - public let toNat16 : (self : Nat8) -> Nat16 = Prim.nat8ToNat16; - - /// Converts a 32-bit unsigned integer to a 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromNat32(123) == (123 : Nat8); - /// ``` - public func fromNat32(x : Nat32) : Nat8 { - Prim.nat16ToNat8(Prim.nat32ToNat16(x)) - }; - - /// Converts an 8-bit unsigned integer to a 32-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toNat32(123) == (123 : Nat32); - /// ``` - public func toNat32(self : Nat8) : Nat32 { - Prim.nat16ToNat32(Prim.nat8ToNat16(self)) - }; - - /// Converts a 64-bit unsigned integer to a 8-bit unsigned integer. - /// - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromNat64(123) == (123 : Nat8); - /// ``` - public func fromNat64(x : Nat64) : Nat8 { - Prim.nat16ToNat8(Prim.nat32ToNat16(Prim.nat64ToNat32(x))) - }; - - /// Converts an 8-bit unsigned integer to a 64-bit unsigned integer. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toNat64(123) == (123 : Nat64); - /// ``` - public func toNat64(self : Nat8) : Nat64 { - Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(self))) - }; - - /// Converts a signed integer with infinite precision to an 8-bit unsigned integer. - /// - /// Wraps on overflow/underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.fromIntWrap(123) == (123 : Nat8); - /// ``` - public let fromIntWrap : Int -> Nat8 = Prim.intToNat8Wrap; - - /// Converts `x` to its textual representation. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.toText(123) == ("123" : Text); - /// ``` - public func toText(self : Nat8) : Text { - Nat.toText(toNat(self)) - }; - - /// Returns the minimum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.min(123, 200) == (123 : Nat8); - /// ``` - public func min(x : Nat8, y : Nat8) : Nat8 { - if (x < y) { x } else { y } - }; - - /// Returns the maximum of `x` and `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.max(123, 200) == (200 : Nat8); - /// ``` - public func max(x : Nat8, y : Nat8) : Nat8 { - if (x < y) { y } else { x } - }; - - /// Equality function for Nat8 types. - /// This is equivalent to `x == y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.equal(1, 1); - /// assert (1 : Nat8) == (1 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let a : Nat8 = 111; - /// let b : Nat8 = 222; - /// assert not Nat8.equal(a, b); - /// ``` - public func equal(x : Nat8, y : Nat8) : Bool { x == y }; - - /// Inequality function for Nat8 types. - /// This is equivalent to `x != y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.notEqual(1, 2); - /// assert (1 : Nat8) != (2 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(x : Nat8, y : Nat8) : Bool { x != y }; - - /// "Less than" function for Nat8 types. - /// This is equivalent to `x < y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.less(1, 2); - /// assert (1 : Nat8) < (2 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(x : Nat8, y : Nat8) : Bool { x < y }; - - /// "Less than or equal" function for Nat8 types. - /// This is equivalent to `x <= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.lessOrEqual(1, 2); - /// assert 1 <= 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(x : Nat8, y : Nat8) : Bool { x <= y }; - - /// "Greater than" function for Nat8 types. - /// This is equivalent to `x > y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.greater(2, 1); - /// assert (2 : Nat8) > (1 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(x : Nat8, y : Nat8) : Bool { x > y }; - - /// "Greater than or equal" function for Nat8 types. - /// This is equivalent to `x >= y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.greaterOrEqual(2, 1); - /// assert (2 : Nat8) >= (1 : Nat8); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(x : Nat8, y : Nat8) : Bool { x >= y }; - - /// General purpose comparison function for `Nat8`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `x` with `y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.compare(2, 3) == #less; - /// ``` - /// - /// This function can be used as value for a high order function, such as a sort function. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.sort([2, 3, 1] : [Nat8], Nat8.compare) == [1, 2, 3]; - /// ``` - public func compare(x : Nat8, y : Nat8) : Order.Order { - if (x < y) { #less } else if (x == y) { #equal } else { - #greater - } - }; - - /// Returns the sum of `x` and `y`, `x + y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.add(1, 2) == 3; - /// assert (1 : Nat8) + (2 : Nat8) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 0, Nat8.add) == 6; - /// ``` - public func add(x : Nat8, y : Nat8) : Nat8 { x + y }; - - /// Returns the difference of `x` and `y`, `x - y`. - /// Traps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.sub(2, 1) == 1; - /// assert (2 : Nat8) - (1 : Nat8) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 20, Nat8.sub) == 14; - /// ``` - public func sub(x : Nat8, y : Nat8) : Nat8 { x - y }; - - /// Returns the product of `x` and `y`, `x * y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.mul(2, 3) == 6; - /// assert (2 : Nat8) * (3 : Nat8) == 6; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// assert Array.foldLeft([2, 3, 1], 1, Nat8.mul) == 6; - /// ``` - public func mul(x : Nat8, y : Nat8) : Nat8 { x * y }; - - /// Returns the quotient of `x` divided by `y`, `x / y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.div(6, 2) == 3; - /// assert (6 : Nat8) / (2 : Nat8) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `/` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `/` - /// as a function value at the moment. - public func div(x : Nat8, y : Nat8) : Nat8 { x / y }; - - /// Returns the remainder of `x` divided by `y`, `x % y`. - /// Traps when `y` is zero. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.rem(6, 4) == 2; - /// assert (6 : Nat8) % (4 : Nat8) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `%` - /// as a function value at the moment. - public func rem(x : Nat8, y : Nat8) : Nat8 { x % y }; - - /// Returns `x` to the power of `y`, `x ** y`. - /// Traps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.pow(2, 3) == 8; - /// assert (2 : Nat8) ** (3 : Nat8) == 8; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**` - /// as a function value at the moment. - public func pow(x : Nat8, y : Nat8) : Nat8 { x ** y }; - - /// Returns the bitwise negation of `x`, `^x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitnot(0) == 255; - /// assert ^(0 : Nat8) == 255; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitnot(x : Nat8) : Nat8 { ^x }; - - /// Returns the bitwise and of `x` and `y`, `x & y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitand(3, 2) == 2; - /// assert (3 : Nat8) & (2 : Nat8) == 2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `&` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `&` - /// as a function value at the moment. - public func bitand(x : Nat8, y : Nat8) : Nat8 { x & y }; - - /// Returns the bitwise or of `x` and `y`, `x | y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitor(3, 2) == 3; - /// assert (3 : Nat8) | (2 : Nat8) == 3; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `|` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `|` - /// as a function value at the moment. - public func bitor(x : Nat8, y : Nat8) : Nat8 { x | y }; - - /// Returns the bitwise exclusive or of `x` and `y`, `x ^ y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitxor(3, 2) == 1; - /// assert (3 : Nat8) ^ (2 : Nat8) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `^` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `^` - /// as a function value at the moment. - public func bitxor(x : Nat8, y : Nat8) : Nat8 { x ^ y }; - - /// Returns the bitwise shift left of `x` by `y`, `x << y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitshiftLeft(1, 2) == 4; - /// assert (1 : Nat8) << (2 : Nat8) == 4; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<` - /// as a function value at the moment. - public func bitshiftLeft(x : Nat8, y : Nat8) : Nat8 { x << y }; - - /// Returns the bitwise shift right of `x` by `y`, `x >> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitshiftRight(4, 2) == 1; - /// assert (4 : Nat8) >> (2 : Nat8) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>>` - /// as a function value at the moment. - public func bitshiftRight(x : Nat8, y : Nat8) : Nat8 { x >> y }; - - /// Returns the bitwise rotate left of `x` by `y`, `x <<> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitrotLeft(128, 1) == 1; - /// assert (128 : Nat8) <<> (1 : Nat8) == 1; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<<>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<<>` - /// as a function value at the moment. - public func bitrotLeft(x : Nat8, y : Nat8) : Nat8 { x <<> y }; - - /// Returns the bitwise rotate right of `x` by `y`, `x <>> y`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitrotRight(1, 1) == 128; - /// assert (1 : Nat8) <>> (1 : Nat8) == 128; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<>>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<>>` - /// as a function value at the moment. - public func bitrotRight(x : Nat8, y : Nat8) : Nat8 { x <>> y }; - - /// Returns the value of bit `p mod 8` in `x`, `(x & 2^(p mod 8)) == 2^(p mod 8)`. - /// This is equivalent to checking if the `p`-th bit is set in `x`, using 0 indexing. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bittest(5, 2); - /// ``` - public func bittest(x : Nat8, p : Nat) : Bool { - Prim.btstNat8(x, Prim.natToNat8(p)) - }; - - /// Returns the value of setting bit `p mod 8` in `x` to `1`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitset(5, 1) == 7; - /// ``` - public func bitset(x : Nat8, p : Nat) : Nat8 { - x | (1 << Prim.natToNat8(p)) - }; - - /// Returns the value of clearing bit `p mod 8` in `x` to `0`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitclear(5, 2) == 1; - /// ``` - public func bitclear(x : Nat8, p : Nat) : Nat8 { - x & ^(1 << Prim.natToNat8(p)) - }; - - /// Returns the value of flipping bit `p mod 8` in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitflip(5, 2) == 1; - /// ``` - public func bitflip(x : Nat8, p : Nat) : Nat8 { - x ^ (1 << Prim.natToNat8(p)) - }; - - /// Returns the count of non-zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitcountNonZero(5) == 2; - /// ``` - public let bitcountNonZero : (x : Nat8) -> Nat8 = Prim.popcntNat8; - - /// Returns the count of leading zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitcountLeadingZero(5) == 5; - /// ``` - public let bitcountLeadingZero : (x : Nat8) -> Nat8 = Prim.clzNat8; - - /// Returns the count of trailing zero bits in `x`. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.bitcountTrailingZero(6) == 1; - /// ``` - public let bitcountTrailingZero : (x : Nat8) -> Nat8 = Prim.ctzNat8; - - /// Returns the sum of `x` and `y`, `x +% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.addWrap(230, 26) == 0; - /// assert (230 : Nat8) +% (26 : Nat8) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `+%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `+%` - /// as a function value at the moment. - public func addWrap(x : Nat8, y : Nat8) : Nat8 { x +% y }; - - /// Returns the difference of `x` and `y`, `x -% y`. Wraps on underflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.subWrap(0, 1) == 255; - /// assert (0 : Nat8) -% (1 : Nat8) == 255; - /// ``` - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `-%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `-%` - /// as a function value at the moment. - public func subWrap(x : Nat8, y : Nat8) : Nat8 { x -% y }; - - /// Returns the product of `x` and `y`, `x *% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.mulWrap(230, 26) == 92; - /// assert (230 : Nat8) *% (26 : Nat8) == 92; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `*%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `*%` - /// as a function value at the moment. - public func mulWrap(x : Nat8, y : Nat8) : Nat8 { x *% y }; - - /// Returns `x` to the power of `y`, `x **% y`. Wraps on overflow. - /// - /// Example: - /// ```motoko include=import - /// assert Nat8.powWrap(2, 8) == 0; - /// assert (2 : Nat8) **% (8 : Nat8) == 0; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `**%` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `**%` - /// as a function value at the moment. - public func powWrap(x : Nat8, y : Nat8) : Nat8 { x **% y }; - - /// Returns an iterator over `Nat8` values from the first to second argument with an exclusive upper bound. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.range(1, 4); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.range(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func range(fromInclusive : Nat8, toExclusive : Nat8) : Iter.Iter { - if (fromInclusive >= toExclusive) { - Iter.empty() - } else { - object { - var n = fromInclusive; - public func next() : ?Nat8 { - if (n == toExclusive) { - null - } else { - let result = n; - n += 1; - ?result - } - } - } - } - }; - - /// Returns an iterator over `Nat8` values from the first to second argument, inclusive. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.rangeInclusive(1, 3); - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// assert iter.next() == ?3; - /// assert iter.next() == null; - /// ``` - /// - /// If the first argument is greater than the second argument, the function returns an empty iterator. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.rangeInclusive(4, 1); - /// assert iter.next() == null; // empty iterator - /// ``` - public func rangeInclusive(from : Nat8, to : Nat8) : Iter.Iter { - if (from > to) { - Iter.empty() - } else { - object { - var n = from; - var done = false; - public func next() : ?Nat8 { - if (done) { - null - } else { - let result = n; - if (n == to) { - done := true - } else { - n += 1 - }; - ?result - } - } - } - } - }; - - /// Returns an iterator over all Nat8 values, from 0 to maxValue. - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// let iter = Nat8.allValues(); - /// assert iter.next() == ?0; - /// assert iter.next() == ?1; - /// assert iter.next() == ?2; - /// // ... - /// ``` - public func allValues() : Iter.Iter { - rangeInclusive(0, maxValue) - }; - -} diff --git a/.mops/core@2.5.0/src/Option.mo b/.mops/core@2.5.0/src/Option.mo deleted file mode 100644 index 27bfce6..0000000 --- a/.mops/core@2.5.0/src/Option.mo +++ /dev/null @@ -1,154 +0,0 @@ -/// Typesafe nullable values. -/// -/// Optional values can be seen as a typesafe `null`. A value of type `?Int` can -/// be constructed with either `null` or `?42`. The simplest way to get at the -/// contents of an optional is to use pattern matching: -/// -/// ```motoko -/// let optionalInt1 : ?Int = ?42; -/// let optionalInt2 : ?Int = null; -/// -/// let int1orZero : Int = switch optionalInt1 { -/// case null 0; -/// case (?int) int; -/// }; -/// assert int1orZero == 42; -/// -/// let int2orZero : Int = switch optionalInt2 { -/// case null 0; -/// case (?int) int; -/// }; -/// assert int2orZero == 0; -/// ``` -/// -/// The functions in this module capture some common operations when working -/// with optionals that can be more succinct than using pattern matching. - -import Runtime "Runtime"; -import Types "Types"; - -module { - - /// Unwraps an optional value, with a default value, i.e. `get(?x, d) = x` and - /// `get(null, d) = d`. - public func get(self : ?T, default : T) : T = switch self { - case null { default }; - case (?x_) { x_ } - }; - - /// Unwraps an optional value using a function, or returns the default, i.e. - /// `option(?x, f, d) = f x` and `option(null, f, d) = d`. - public func getMapped(self : ?T, f : T -> R, default : R) : R = switch self { - case null { default }; - case (?x_) { f(x_) } - }; - - /// Applies a function to the wrapped value. `null`'s are left untouched. - /// ```motoko - /// import Option "mo:core/Option"; - /// assert Option.map(?42, func x = x + 1) == ?43; - /// assert Option.map(null, func x = x + 1) == null; - /// ``` - public func map(self : ?T, f : T -> R) : ?R = switch self { - case null { null }; - case (?x_) { ?f(x_) } - }; - - /// Applies a function to the wrapped value, but discards the result. Use - /// `forEach` if you're only interested in the side effect `f` produces. - /// - /// ```motoko - /// import Option "mo:core/Option"; - /// var counter : Nat = 0; - /// Option.forEach(?5, func (x : Nat) { counter += x }); - /// assert counter == 5; - /// Option.forEach(null, func (x : Nat) { counter += x }); - /// assert counter == 5; - /// ``` - public func forEach(self : ?T, f : T -> ()) = switch self { - case null {}; - case (?x_) { f(x_) } - }; - - /// Applies an optional function to an optional value. Returns `null` if at - /// least one of the arguments is `null`. - public func apply(self : ?T, f : ?(T -> R)) : ?R { - switch (f, self) { - case (?f_, ?x_) { ?f_(x_) }; - case (_, _) { null } - } - }; - - /// Applies a function to an optional value. Returns `null` if the argument is - /// `null`, or the function returns `null`. - public func chain(self : ?T, f : T -> ?R) : ?R { - switch (self) { - case (?x_) { f(x_) }; - case (null) { null } - } - }; - - /// Given an optional optional value, removes one layer of optionality. - /// ```motoko - /// import Option "mo:core/Option"; - /// assert Option.flatten(?(?(42))) == ?42; - /// assert Option.flatten(?(null)) == null; - /// assert Option.flatten(null) == null; - /// ``` - public func flatten(self : ??T) : ?T { - chain(self, func(x_ : ?T) : ?T = x_) - }; - - /// Creates an optional value from a definite value. - /// ```motoko - /// import Option "mo:core/Option"; - /// assert Option.some(42) == ?42; - /// ``` - public func some(self : T) : ?T = ?self; - - /// Returns true if the argument is not `null`, otherwise returns false. - public func isSome(self : ?Any) : Bool { - self != null - }; - - /// Returns true if the argument is `null`, otherwise returns false. - public func isNull(self : ?Any) : Bool { - self == null - }; - - /// Returns true if the optional arguments are equal according to the equality function provided, otherwise returns false. - public func equal(self : ?T, other : ?T, eq : (implicit : (equal : (T, T) -> Bool))) : Bool = switch (self, other) { - case (null, null) { true }; - case (?x_, ?y_) { eq(x_, y_) }; - case (_, _) { false } - }; - - /// Compares two optional values using the provided comparison function. - /// - /// Returns: - /// - `#equal` if both values are `null`, - /// - `#less` if the first value is `null` and the second is not, - /// - `#greater` if the first value is not `null` and the second is, - /// - the result of the comparison function when both values are not `null`. - public func compare(self : ?T, other : ?T, compare : (implicit : (T, T) -> Types.Order)) : Types.Order = switch (self, other) { - case (null, null) #equal; - case (null, _) #less; - case (_, null) #greater; - case (?x_, ?y_) { compare(x_, y_) } - }; - - /// Unwraps an optional value, i.e. `unwrap(?x) = x`. - /// - /// `Option.unwrap()` fails if the argument is null. Consider using a `switch` or `do?` expression instead. - public func unwrap(self : ?T) : T = switch self { - case null { Runtime.trap("Option.unwrap()") }; - case (?x_) { x_ } - }; - - /// Returns the textural representation of an optional value for debugging purposes. - public func toText(self : ?T, toText : (implicit : T -> Text)) : Text = switch self { - case null { "null" }; - case (?x_) { "?" # toText(x_) } - }; - -} diff --git a/.mops/core@2.5.0/src/Order.mo b/.mops/core@2.5.0/src/Order.mo deleted file mode 100644 index d708a11..0000000 --- a/.mops/core@2.5.0/src/Order.mo +++ /dev/null @@ -1,62 +0,0 @@ -/// Utilities for `Order` (comparison between two values). - -import Types "Types"; - -module { - - /// A type to represent an order. - public type Order = Types.Order; - - /// Check if an order is #less. - public func isLess(self : Order) : Bool { - switch self { - case (#less) { true }; - case _ { false } - } - }; - - /// Check if an order is #equal. - public func isEqual(self : Order) : Bool { - switch self { - case (#equal) { true }; - case _ { false } - } - }; - - /// Check if an order is #greater. - public func isGreater(self : Order) : Bool { - switch self { - case (#greater) { true }; - case _ { false } - } - }; - - /// Returns true if only if `order1` and `order2` are the same. - public func equal(self : Order, other : Order) : Bool { - switch (self, other) { - case (#less, #less) { true }; - case (#equal, #equal) { true }; - case (#greater, #greater) { true }; - case _ { false } - } - }; - - /// Returns an iterator that yields all possible `Order` values: - /// `#less`, `#equal`, `#greater`. - public func allValues() : Types.Iter { - var nextState : ?Order = ?#less; - { - next = func() : ?Order { - let state = nextState; - switch state { - case (?#less) { nextState := ?#equal }; - case (?#equal) { nextState := ?#greater }; - case (?#greater) { nextState := null }; - case (null) {} - }; - state - } - } - } - -} diff --git a/.mops/core@2.5.0/src/Principal.mo b/.mops/core@2.5.0/src/Principal.mo deleted file mode 100644 index 974add1..0000000 --- a/.mops/core@2.5.0/src/Principal.mo +++ /dev/null @@ -1,1297 +0,0 @@ -/// Module for interacting with Principals (users and canisters). -/// -/// Principals are used to identify entities that can interact with the Internet -/// Computer. These entities are either users or canisters. -/// -/// Example textual representation of Principals: -/// -/// `un4fu-tqaaa-aaaab-qadjq-cai` -/// -/// In Motoko, there is a primitive Principal type called `Principal`. As an example -/// of where you might see Principals, you can access the Principal of the -/// caller of your shared function. -/// -/// ```motoko no-repl -/// persistent actor { -/// public shared(msg) func foo() { -/// let caller : Principal = msg.caller; -/// }; -/// } -/// ``` -/// -/// Then, you can use this module to work with the `Principal`. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Principal "mo:core/Principal"; -/// ``` - -import Prim "mo:⛔"; -import Blob "Blob"; -import Array "Array"; -import VarArray "VarArray"; -import Nat8 "Nat8"; -import Nat32 "Nat32"; -import Nat64 "Nat64"; -import Text "Text"; -import Types "Types"; - -module { - - public type Principal = Prim.Types.Principal; - - /// Get the `Principal` identifier of an actor. - /// - /// Example: - /// ```motoko include=import no-repl - /// persistent actor MyCanister { - /// func getPrincipal() : Principal { - /// let principal = Principal.fromActor(MyCanister); - /// } - /// } - /// ``` - public let fromActor : (a : actor {}) -> Principal = Prim.principalOfActor; - - /// Turns a `Principal` into an actor reference. The presence of the methods - /// (and their respective types) is not guaranteed. - /// - /// This is the inverse of `fromActor`. The returned reference is - /// typed with the given actor type `A`. - /// - /// Example: - /// ```motoko include=import no-repl - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let canister : actor {} = Principal.toActor(principal); - /// ``` - public let toActor : (p : Principal) -> A = Prim.actorOfPrincipal; - - /// Compute the Ledger account identifier of a principal. Optionally specify a sub-account. - /// - /// Example: - /// ```motoko include=import no-validate - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let subAccount : Blob = "\4A\8D\3F\2B\6E\01\C8\7D\9E\03\B4\56\7C\F8\9A\01\D2\34\56\78\9A\BC\DE\F0\12\34\56\78\9A\BC\DE\F0"; - /// let account = Principal.toLedgerAccount(principal, ?subAccount); - /// assert account == "\8C\5C\20\C6\15\3F\7F\51\E2\0D\0F\0F\B5\08\51\5B\47\65\63\A9\62\B4\A9\91\5F\4F\02\70\8A\ED\4F\82"; - /// ``` - public func toLedgerAccount(self : Principal, subAccount : ?Blob) : Blob { - let sha224 = SHA224(); - let accountSeparator : Blob = "\0Aaccount-id"; - sha224.writeBlob(accountSeparator); - sha224.writeBlob(toBlob(self)); - switch subAccount { - case (?subAccount) { - sha224.writeBlob(subAccount) - }; - case (null) { - let defaultSubAccount = Array.tabulate(32, func _ = 0); - sha224.writeArray(defaultSubAccount) - } - }; - - let hashSum = sha224.sum(); - - // hashBlob is a CRC32 implementation - let crc32Bytes = nat32ToByteArray(Prim.hashBlob hashSum); - - Blob.fromArray(Array.concat(crc32Bytes, Blob.toArray(hashSum))) - }; - - /// Convert a `Principal` to its `Blob` (bytes) representation. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let blob = Principal.toBlob(principal); - /// assert blob == "\00\00\00\00\00\30\00\D3\01\01"; - /// ``` - public let toBlob : (self : Principal) -> Blob = Prim.blobOfPrincipal; - - /// Converts a `Blob` (bytes) representation of a `Principal` to a `Principal` value. - /// - /// Example: - /// ```motoko include=import - /// let blob = "\00\00\00\00\00\30\00\D3\01\01" : Blob; - /// let principal = Principal.fromBlob(blob); - /// assert Principal.toText(principal) == "un4fu-tqaaa-aaaab-qadjq-cai"; - /// ``` - public let fromBlob : (self : Blob) -> Principal = Prim.principalOfBlob; - - /// Converts a `Principal` to its `Text` representation. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.toText(principal) == "un4fu-tqaaa-aaaab-qadjq-cai"; - /// ``` - public func toText(self : Principal) : Text = debug_show (self); - - /// Converts a `Text` representation of a `Principal` to a `Principal` value. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.toText(principal) == "un4fu-tqaaa-aaaab-qadjq-cai"; - /// ``` - public func fromText(t : Text) : Principal = fromActor(actor (t)); - - private let anonymousBlob : Blob = "\04"; - - /// Constructs and returns the anonymous principal. - public func anonymous() : Principal = Prim.principalOfBlob(anonymousBlob); - - /// Checks if the given principal represents an anonymous user. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert not Principal.isAnonymous(principal); - /// ``` - public func isAnonymous(self : Principal) : Bool = Prim.blobOfPrincipal self == anonymousBlob; - - /// Checks if the given principal is a canister. - /// - /// The last byte for opaque principal ids must be 0x01 - /// https://internetcomputer.org/docs/current/references/ic-interface-spec#principal - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.isCanister(principal); - /// ``` - public func isCanister(self : Principal) : Bool { - let byteArray = toByteArray(self); - - byteArray.size() >= 0 and byteArray.size() <= 29 and isLastByte(byteArray, 1) - }; - - /// Checks if the given principal is a self authenticating principal. - /// Most of the time, this is a user principal. - /// - /// The last byte for user principal ids must be 0x02 - /// https://internetcomputer.org/docs/current/references/ic-interface-spec#principal - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("6rgy7-3uukz-jrj2k-crt3v-u2wjm-dmn3t-p26d6-ndilt-3gusv-75ybk-jae"); - /// assert Principal.isSelfAuthenticating(principal); - /// ``` - public func isSelfAuthenticating(self : Principal) : Bool { - let byteArray = toByteArray(self); - - byteArray.size() == 29 and isLastByte(byteArray, 2) - }; - - /// Checks if the given principal is a reserved principal. - /// - /// The last byte for reserved principal ids must be 0x7f - /// https://internetcomputer.org/docs/current/references/ic-interface-spec#principal - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert not Principal.isReserved(principal); - /// ``` - public func isReserved(self : Principal) : Bool { - let byteArray = toByteArray(self); - - byteArray.size() >= 0 and byteArray.size() <= 29 and isLastByte(byteArray, 127) - }; - - /// Checks if the given principal can control this canister. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert not Principal.isController(principal); - /// ``` - public func isController(self : Principal) : Bool = Prim.isController self; - - /// Hashes the given principal by hashing its `Blob` representation. - /// - /// Example: - /// ```motoko include=import - /// let principal = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.hash(principal) == 2_742_573_646; - /// ``` - public func hash(self : Principal) : Types.Hash = Blob.hash(Prim.blobOfPrincipal(self)); - - /// General purpose comparison function for `Principal`. Returns the `Order` ( - /// either `#less`, `#equal`, or `#greater`) of comparing `principal1` with - /// `principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// assert Principal.compare(principal1, principal2) == #equal; - /// ``` - public func compare(self : Principal, other : Principal) : { - #less; - #equal; - #greater - } { - if (self < other) { - #less - } else if (self == other) { - #equal - } else { - #greater - } - }; - - /// Equality function for Principal types. - /// This is equivalent to `principal1 == principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.equal(principal1, principal2); - /// assert principal1 == principal2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `==` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `==` - /// as a function value at the moment. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.anonymous(); - /// let principal2 = Principal.fromBlob("\04"); - /// assert Principal.equal(principal1, principal2); - /// ``` - public func equal(self : Principal, other : Principal) : Bool { - self == other - }; - - /// Inequality function for Principal types. - /// This is equivalent to `principal1 != principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.notEqual(principal1, principal2); - /// assert not (principal1 != principal2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `!=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `!=` - /// as a function value at the moment. - public func notEqual(self : Principal, other : Principal) : Bool { - self != other - }; - - /// "Less than" function for Principal types. - /// This is equivalent to `principal1 < principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.less(principal1, principal2); - /// assert not (principal1 < principal2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<` - /// as a function value at the moment. - public func less(self : Principal, other : Principal) : Bool { - self < other - }; - - /// "Less than or equal to" function for Principal types. - /// This is equivalent to `principal1 <= principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.lessOrEqual(principal1, principal2); - /// assert principal1 <= principal2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `<=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `<=` - /// as a function value at the moment. - public func lessOrEqual(self : Principal, other : Principal) : Bool { - self <= other - }; - - /// "Greater than" function for Principal types. - /// This is equivalent to `principal1 > principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.greater(principal1, principal2); - /// assert not (principal1 > principal2); - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>` - /// as a function value at the moment. - public func greater(self : Principal, other : Principal) : Bool { - self > other - }; - - /// "Greater than or equal to" function for Principal types. - /// This is equivalent to `principal1 >= principal2`. - /// - /// Example: - /// ```motoko include=import - /// let principal1 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// let principal2 = Principal.fromText("un4fu-tqaaa-aaaab-qadjq-cai"); - /// ignore Principal.greaterOrEqual(principal1, principal2); - /// assert principal1 >= principal2; - /// ``` - /// - /// Note: The reason why this function is defined in this library (in addition - /// to the existing `>=` operator) is so that you can use it as a function - /// value to pass to a higher order function. It is not possible to use `>=` - /// as a function value at the moment. - public func greaterOrEqual(self : Principal, other : Principal) : Bool { - self >= other - }; - - /** - * SHA224 Utilities used in toAccount(). - * Utilities are not exposed as public functions. - * Taken with permission from https://github.com/research-ag/sha2 - **/ - let K00 : Nat32 = 0x428a2f98; - let K01 : Nat32 = 0x71374491; - let K02 : Nat32 = 0xb5c0fbcf; - let K03 : Nat32 = 0xe9b5dba5; - let K04 : Nat32 = 0x3956c25b; - let K05 : Nat32 = 0x59f111f1; - let K06 : Nat32 = 0x923f82a4; - let K07 : Nat32 = 0xab1c5ed5; - let K08 : Nat32 = 0xd807aa98; - let K09 : Nat32 = 0x12835b01; - let K10 : Nat32 = 0x243185be; - let K11 : Nat32 = 0x550c7dc3; - let K12 : Nat32 = 0x72be5d74; - let K13 : Nat32 = 0x80deb1fe; - let K14 : Nat32 = 0x9bdc06a7; - let K15 : Nat32 = 0xc19bf174; - let K16 : Nat32 = 0xe49b69c1; - let K17 : Nat32 = 0xefbe4786; - let K18 : Nat32 = 0x0fc19dc6; - let K19 : Nat32 = 0x240ca1cc; - let K20 : Nat32 = 0x2de92c6f; - let K21 : Nat32 = 0x4a7484aa; - let K22 : Nat32 = 0x5cb0a9dc; - let K23 : Nat32 = 0x76f988da; - let K24 : Nat32 = 0x983e5152; - let K25 : Nat32 = 0xa831c66d; - let K26 : Nat32 = 0xb00327c8; - let K27 : Nat32 = 0xbf597fc7; - let K28 : Nat32 = 0xc6e00bf3; - let K29 : Nat32 = 0xd5a79147; - let K30 : Nat32 = 0x06ca6351; - let K31 : Nat32 = 0x14292967; - let K32 : Nat32 = 0x27b70a85; - let K33 : Nat32 = 0x2e1b2138; - let K34 : Nat32 = 0x4d2c6dfc; - let K35 : Nat32 = 0x53380d13; - let K36 : Nat32 = 0x650a7354; - let K37 : Nat32 = 0x766a0abb; - let K38 : Nat32 = 0x81c2c92e; - let K39 : Nat32 = 0x92722c85; - let K40 : Nat32 = 0xa2bfe8a1; - let K41 : Nat32 = 0xa81a664b; - let K42 : Nat32 = 0xc24b8b70; - let K43 : Nat32 = 0xc76c51a3; - let K44 : Nat32 = 0xd192e819; - let K45 : Nat32 = 0xd6990624; - let K46 : Nat32 = 0xf40e3585; - let K47 : Nat32 = 0x106aa070; - let K48 : Nat32 = 0x19a4c116; - let K49 : Nat32 = 0x1e376c08; - let K50 : Nat32 = 0x2748774c; - let K51 : Nat32 = 0x34b0bcb5; - let K52 : Nat32 = 0x391c0cb3; - let K53 : Nat32 = 0x4ed8aa4a; - let K54 : Nat32 = 0x5b9cca4f; - let K55 : Nat32 = 0x682e6ff3; - let K56 : Nat32 = 0x748f82ee; - let K57 : Nat32 = 0x78a5636f; - let K58 : Nat32 = 0x84c87814; - let K59 : Nat32 = 0x8cc70208; - let K60 : Nat32 = 0x90befffa; - let K61 : Nat32 = 0xa4506ceb; - let K62 : Nat32 = 0xbef9a3f7; - let K63 : Nat32 = 0xc67178f2; - - let ivs : [[Nat32]] = [ - [ - // 224 - 0xc1059ed8, - 0x367cd507, - 0x3070dd17, - 0xf70e5939, - 0xffc00b31, - 0x68581511, - 0x64f98fa7, - 0xbefa4fa4 - ], - [ - // 256 - 0x6a09e667, - 0xbb67ae85, - 0x3c6ef372, - 0xa54ff53a, - 0x510e527f, - 0x9b05688c, - 0x1f83d9ab, - 0x5be0cd19 - ] - ]; - - let rot = Nat32.bitrotRight; - - class SHA224() { - let (sum_bytes, iv) = (28, 0); - - var s0 : Nat32 = 0; - var s1 : Nat32 = 0; - var s2 : Nat32 = 0; - var s3 : Nat32 = 0; - var s4 : Nat32 = 0; - var s5 : Nat32 = 0; - var s6 : Nat32 = 0; - var s7 : Nat32 = 0; - - let msg : [var Nat32] = VarArray.repeat(0, 16); - let digest = VarArray.repeat(0, sum_bytes); - var word : Nat32 = 0; - - var i_msg : Nat8 = 0; - var i_byte : Nat8 = 4; - var i_block : Nat64 = 0; - - public func reset() { - i_msg := 0; - i_byte := 4; - i_block := 0; - s0 := ivs[iv][0]; - s1 := ivs[iv][1]; - s2 := ivs[iv][2]; - s3 := ivs[iv][3]; - s4 := ivs[iv][4]; - s5 := ivs[iv][5]; - s6 := ivs[iv][6]; - s7 := ivs[iv][7] - }; - - reset(); - - private func writeByte(val : Nat8) : () { - word := (word << 8) ^ Nat32.fromIntWrap(Nat8.toNat(val)); - i_byte -%= 1; - if (i_byte == 0) { - msg[Nat8.toNat(i_msg)] := word; - word := 0; - i_byte := 4; - i_msg +%= 1; - if (i_msg == 16) { - process_block(); - i_msg := 0; - i_block +%= 1 - } - } - }; - - private func process_block() : () { - let w00 = msg[0]; - let w01 = msg[1]; - let w02 = msg[2]; - let w03 = msg[3]; - let w04 = msg[4]; - let w05 = msg[5]; - let w06 = msg[6]; - let w07 = msg[7]; - let w08 = msg[8]; - let w09 = msg[9]; - let w10 = msg[10]; - let w11 = msg[11]; - let w12 = msg[12]; - let w13 = msg[13]; - let w14 = msg[14]; - let w15 = msg[15]; - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - /* - for ((i, j, k, l, m) in expansion_rounds.values()) { - // (j,k,l,m) = (i+1,i+9,i+14,i+16) - let (v0, v1) = (msg[j], msg[l]); - let s0 = rot(v0, 07) ^ rot(v0, 18) ^ (v0 >> 03); - let s1 = rot(v1, 17) ^ rot(v1, 19) ^ (v1 >> 10); - msg[m] := msg[i] +% s0 +% msg[k] +% s1; - }; - */ - // compress - var a = s0; - var b = s1; - var c = s2; - var d = s3; - var e = s4; - var f = s5; - var g = s6; - var h = s7; - var t = 0 : Nat32; - - t := h +% K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K09 +% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K10 +% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K11 +% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K12 +% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K13 +% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K14 +% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - - /* - for (i in compression_rounds.keys()) { - let ch = (e & f) ^ (^ e & g); - let maj = (a & b) ^ (a & c) ^ (b & c); - let sigma0 = rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - let sigma1 = rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - let t = h +% K[i] +% msg[i] +% ch +% sigma1; - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% maj +% sigma0; - }; - */ - // final addition - s0 +%= a; - s1 +%= b; - s2 +%= c; - s3 +%= d; - s4 +%= e; - s5 +%= f; - s6 +%= g; - s7 +%= h - }; - - public func writeIter(iter : { next() : ?Nat8 }) : () { - label reading loop { - switch (iter.next()) { - case (?val) { - writeByte(val); - continue reading - }; - case (null) { - break reading - } - } - } - }; - - public func writeArray(arr : [Nat8]) : () = writeIter(arr.vals()); - public func writeBlob(blob : Blob) : () = writeIter(blob.vals()); - - public func sum() : Blob { - // calculate padding - // t = bytes in the last incomplete block (0-63) - let t : Nat8 = (i_msg << 2) +% 4 -% i_byte; - // p = length of padding (1-64) - var p : Nat8 = if (t < 56) (56 -% t) else (120 -% t); - // n_bits = length of message in bits - let n_bits : Nat64 = ((i_block << 6) +% Nat64.fromIntWrap(Nat8.toNat(t))) << 3; - - // write padding - writeByte(0x80); - p -%= 1; - while (p != 0) { - writeByte(0x00); - p -%= 1 - }; - - // write length (8 bytes) - // Note: this exactly fills the block buffer, hence process_block will get - // triggered by the last writeByte - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 56) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 48) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 40) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 32) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 24) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 16) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat((n_bits >> 8) & 0xff))); - writeByte(Nat8.fromIntWrap(Nat64.toNat(n_bits & 0xff))); - - // retrieve sum - digest[0] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 24) & 0xff)); - digest[1] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 16) & 0xff)); - digest[2] := Nat8.fromIntWrap(Nat32.toNat((s0 >> 8) & 0xff)); - digest[3] := Nat8.fromIntWrap(Nat32.toNat(s0 & 0xff)); - digest[4] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 24) & 0xff)); - digest[5] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 16) & 0xff)); - digest[6] := Nat8.fromIntWrap(Nat32.toNat((s1 >> 8) & 0xff)); - digest[7] := Nat8.fromIntWrap(Nat32.toNat(s1 & 0xff)); - digest[8] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 24) & 0xff)); - digest[9] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 16) & 0xff)); - digest[10] := Nat8.fromIntWrap(Nat32.toNat((s2 >> 8) & 0xff)); - digest[11] := Nat8.fromIntWrap(Nat32.toNat(s2 & 0xff)); - digest[12] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 24) & 0xff)); - digest[13] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 16) & 0xff)); - digest[14] := Nat8.fromIntWrap(Nat32.toNat((s3 >> 8) & 0xff)); - digest[15] := Nat8.fromIntWrap(Nat32.toNat(s3 & 0xff)); - digest[16] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 24) & 0xff)); - digest[17] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 16) & 0xff)); - digest[18] := Nat8.fromIntWrap(Nat32.toNat((s4 >> 8) & 0xff)); - digest[19] := Nat8.fromIntWrap(Nat32.toNat(s4 & 0xff)); - digest[20] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 24) & 0xff)); - digest[21] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 16) & 0xff)); - digest[22] := Nat8.fromIntWrap(Nat32.toNat((s5 >> 8) & 0xff)); - digest[23] := Nat8.fromIntWrap(Nat32.toNat(s5 & 0xff)); - digest[24] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 24) & 0xff)); - digest[25] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 16) & 0xff)); - digest[26] := Nat8.fromIntWrap(Nat32.toNat((s6 >> 8) & 0xff)); - digest[27] := Nat8.fromIntWrap(Nat32.toNat(s6 & 0xff)); - - return Blob.fromVarArray(digest) - } - }; // class SHA224 - - func nat32ToByteArray(n : Nat32) : [Nat8] { - func byte(n : Nat32) : Nat8 { - Nat8.fromNat(Nat32.toNat(n & 0xff)) - }; - [byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)] - }; - - func toByteArray(p : Principal) : [Nat8] = Blob.toArray(toBlob(p)); - - func isLastByte(byteArray : [Nat8], byte : Nat8) : Bool { - let size = byteArray.size(); - size > 0 and byteArray[size - 1] == byte - } -} diff --git a/.mops/core@2.5.0/src/PriorityQueue.mo b/.mops/core@2.5.0/src/PriorityQueue.mo deleted file mode 100644 index 4045b4f..0000000 --- a/.mops/core@2.5.0/src/PriorityQueue.mo +++ /dev/null @@ -1,299 +0,0 @@ -/// A mutable priority queue of elements. -/// Always returns the element with the highest priority first, -/// as determined by a user-provided comparison function. -/// -/// Typical use cases include: -/// * Task scheduling (highest-priority task first) -/// * Event simulation -/// * Pathfinding algorithms (e.g. Dijkstra, A*) -/// -/// Example: -/// ```motoko -/// import PriorityQueue "mo:core/PriorityQueue"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// let pq = PriorityQueue.empty(); -/// PriorityQueue.push(pq, Nat.compare, 5); -/// PriorityQueue.push(pq, Nat.compare, 10); -/// PriorityQueue.push(pq, Nat.compare, 3); -/// assert PriorityQueue.pop(pq, Nat.compare) == ?10; -/// assert PriorityQueue.pop(pq, Nat.compare) == ?5; -/// assert PriorityQueue.pop(pq, Nat.compare) == ?3; -/// assert PriorityQueue.pop(pq, Nat.compare) == null; -/// } -/// ``` -/// -/// Internally implemented as a binary heap stored in a core library `List`. -/// -/// Performance: -/// * Runtime: `O(log n)` for `push` and `pop` (amortized). -/// * Runtime: `O(1)` for `peek`, `clear`, `size`, and `isEmpty`. -/// * Space: `O(n)`, where `n` is the number of stored elements. -/// -/// Implementation note (due to `List`): -/// * There is an additive memory overhead of `O(sqrt(n))`. -/// * For `push` and `pop`, the amortized time is `O(log n)`, -/// but the worst case can involve an extra `O(sqrt(n))` step. -import List "List"; -import Types "Types"; -import Order "Order"; - -module { - public type PriorityQueue = Types.PriorityQueue; - - /// Returns an empty priority queue. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// - /// let pq = PriorityQueue.empty(); - /// assert PriorityQueue.isEmpty(pq); - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func empty() : PriorityQueue = { - heap = List.empty() - }; - - /// Returns a priority queue containing a single element. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// - /// let pq = PriorityQueue.singleton(42); - /// assert PriorityQueue.peek(pq) == ?42; - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func singleton(element : T) : PriorityQueue = { - heap = List.singleton(element) - }; - - /// Returns the number of elements in the priority queue. - /// - /// Runtime: `O(1)`. - public func size(self : PriorityQueue) : Nat = List.size(self.heap); - - /// Returns `true` iff the priority queue is empty. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let pq = PriorityQueue.empty(); - /// assert PriorityQueue.isEmpty(pq); - /// PriorityQueue.push(pq, Nat.compare, 5); - /// assert not PriorityQueue.isEmpty(pq); - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func isEmpty(self : PriorityQueue) : Bool = List.isEmpty(self.heap); - - /// Removes all elements from the priority queue. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// - /// let pq = PriorityQueue.empty(); - /// PriorityQueue.push(pq, Nat.compare, 5); - /// PriorityQueue.push(pq, Nat.compare, 10); - /// assert not PriorityQueue.isEmpty(pq); - /// PriorityQueue.clear(pq); - /// assert PriorityQueue.isEmpty(pq); - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func clear(self : PriorityQueue) = List.clear(self.heap); - - /// Inserts a new element into the priority queue. - /// - /// `compare` – comparison function that defines priority ordering. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let pq = PriorityQueue.empty(); - /// PriorityQueue.push(pq, Nat.compare, 5); - /// PriorityQueue.push(pq, Nat.compare, 10); - /// assert PriorityQueue.peek(pq) == ?10; - /// ``` - /// - /// Runtime: `O(log n)`. Space: `O(1)`. - public func push( - self : PriorityQueue, - compare : (implicit : (T, T) -> Order.Order), - element : T - ) { - let heap = self.heap; - List.add(heap, element); - var index : Nat = List.size(heap) - 1; - while (index > 0) { - let parentId = (index - 1) : Nat / 2; - let parentVal = List.at(heap, parentId); - if (compare(element, parentVal) == #greater) { - List.put(heap, index, parentVal); - index := parentId - } else { - List.put(heap, index, element); - return - } - }; - List.put(heap, 0, element) - }; - - /// Returns the element with the highest priority, without removing it. - /// Returns `null` if the queue is empty. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// - /// let pq = PriorityQueue.singleton(42); - /// assert PriorityQueue.peek(pq) == ?42; - /// ``` - /// - /// Runtime: `O(1)`. Space: `O(1)`. - public func peek(self : PriorityQueue) : ?T = List.get(self.heap, 0); - - /// Removes and returns the element with the highest priority. - /// Returns `null` if the queue is empty. - /// - /// `compare` – comparison function that defines priority ordering. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let pq = PriorityQueue.empty(); - /// PriorityQueue.push(pq, Nat.compare, 5); - /// PriorityQueue.push(pq, Nat.compare, 10); - /// assert PriorityQueue.pop(pq, Nat.compare) == ?10; - /// ``` - /// - /// Runtime: `O(log n)`. Space: `O(1)`. - public func pop( - self : PriorityQueue, - compare : (implicit : (T, T) -> Order.Order) - ) : ?T { - let heap = self.heap; - if (List.isEmpty(heap)) { - return null - }; - let top = List.get(heap, 0); - let lastIndex : Nat = List.size(heap) - 1; - let lastElem = List.at(heap, lastIndex); - - var index = 0; - loop { - var best = lastIndex; - let left = 2 * index + 1; - var bestElem = lastElem; - if (left < lastIndex) { - let leftElem = List.at(heap, left); - if (compare(leftElem, lastElem) == #greater) { - best := left; - bestElem := leftElem - } - }; - let right = left + 1; - if (right < lastIndex) { - let rightElem = List.at(heap, right); - if (compare(rightElem, bestElem) == #greater) { - best := right; - bestElem := rightElem - } - }; - if (best == lastIndex) { - List.put(heap, index, lastElem); - ignore List.removeLast(heap); - return top - }; - List.put(heap, index, bestElem); - index := best - } - }; - - /// Creates a new priority queue from an iterator. - /// - /// `compare` – comparison function that defines priority ordering. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let pq = PriorityQueue.fromIter([5, 10, 3].values(), Nat.compare); - /// assert PriorityQueue.size(pq) == 3; - /// assert PriorityQueue.peek(pq) == ?10; - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// `n` denotes the number of elements in the iterator. - public func fromIter(iter : Types.Iter, compare : (implicit : (T, T) -> Order.Order)) : PriorityQueue { - let pq = empty(); - for (element in iter) { - push(pq, element) - }; - pq - }; - - /// Creates a copy of the priority queue. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// - /// let original = PriorityQueue.fromIter([5, 10, 3].values(), Nat.compare); - /// let copy = PriorityQueue.clone(original); - /// assert PriorityQueue.pop(copy, Nat.compare) == ?10; - /// assert PriorityQueue.size(original) == 3; - /// ``` - /// - /// Runtime: `O(n)`. Space: `O(n)`. - /// `n` denotes the number of elements in the priority queue. - public func clone(self : PriorityQueue) : PriorityQueue = { - heap = List.clone(self.heap) - }; - - /// Returns an iterator that yields elements in descending priority order - /// (highest priority first, matching `pop` semantics). - /// - /// The original queue is not modified. Internally clones the heap - /// and pops from the clone on each `next()` call. - /// - /// `compare` – comparison function that defines priority ordering. - /// - /// Example: - /// ```motoko - /// import PriorityQueue "mo:core/PriorityQueue"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// let pq = PriorityQueue.fromIter([5, 10, 3].values(), Nat.compare); - /// assert Iter.toArray(PriorityQueue.values(pq, Nat.compare)) == [10, 5, 3]; - /// ``` - /// - /// Runtime: `O(n)` to create the iterator, `O(log n)` per `next()` call. - /// Space: `O(n)` for the internal clone. - /// `n` denotes the number of elements in the priority queue. - public func values(self : PriorityQueue, compare : (implicit : (T, T) -> Order.Order)) : Types.Iter { - let copy : PriorityQueue = clone(self); - object { - public func next() : ?T { - pop(copy) - } - } - } -} diff --git a/.mops/core@2.5.0/src/Queue.mo b/.mops/core@2.5.0/src/Queue.mo deleted file mode 100644 index d8f48c5..0000000 --- a/.mops/core@2.5.0/src/Queue.mo +++ /dev/null @@ -1,820 +0,0 @@ -/// A mutable double-ended queue of elements. -/// The queue has two ends, front and back. -/// Elements can be added and removed at the two ends. -/// -/// This can be used for different use cases, such as: -/// * Queue (FIFO) by using `pushBack()` and `popFront()` -/// * Stack (LIFO) by using `pushFront()` and `popFront()`. -/// -/// Example: -/// ```motoko -/// import Queue "mo:core/Queue"; -/// -/// persistent actor { -/// let orders = Queue.empty(); -/// Queue.pushBack(orders, "Motoko"); -/// Queue.pushBack(orders, "Mops"); -/// Queue.pushBack(orders, "IC"); -/// assert Queue.popFront(orders) == ?"Motoko"; -/// assert Queue.popFront(orders) == ?"Mops"; -/// assert Queue.popFront(orders) == ?"IC"; -/// assert Queue.popFront(orders) == null; -/// } -/// ``` -/// -/// The internal implementation is a doubly-linked list. -/// -/// Performance: -/// * Runtime: `O(1)` for push, pop, and peek operations. -/// * Space: `O(n)`. -/// `n` denotes the number of elements stored in the queue. - -import PureQueue "pure/Queue"; -import Iter "Iter"; -import Order "Order"; -import Types "Types"; -import Array "Array"; -import Prim "mo:⛔"; - -module { - public type Queue = Types.Queue.Queue; - - type Node = Types.Queue.Node; - - /// Converts a mutable queue to an immutable, purely functional queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let pureQueue = Queue.toPure(queue); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - /// @deprecated M0235 - public func toPure(self : Queue) : PureQueue.Queue { - let pureQueue = PureQueue.empty(); - let iter = values(self); - var current = pureQueue; - loop { - switch (iter.next()) { - case null { return current }; - case (?val) { current := PureQueue.pushBack(current, val) } - } - } - }; - - /// Converts an immutable, purely functional queue to a mutable queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import PureQueue "mo:core/pure/Queue"; - /// - /// persistent actor { - /// let pureQueue = PureQueue.fromIter([1, 2, 3].values()); - /// let queue = Queue.fromPure(pureQueue); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - /// @deprecated M0235 - public func fromPure(pureQueue : PureQueue.Queue) : Queue { - let queue = empty(); - let iter = PureQueue.values(pureQueue); - loop { - switch (iter.next()) { - case null { return queue }; - case (?val) { pushBack(queue, val) } - } - } - }; - - /// Create a new empty mutable double-ended queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.size(queue) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Queue { - { var front = null; var back = null; var size = 0 } - }; - - /// Creates a new queue with a single element. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.singleton(123); - /// assert Queue.size(queue) == 1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func singleton(element : T) : Queue { - let queue = empty(); - pushBack(queue, element); - queue - }; - - /// Removes all elements from the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// Queue.clear(queue); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func clear(self : Queue) { - self.front := null; - self.back := null; - self.size := 0 - }; - - /// Creates a deep copy of the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let original = Queue.fromIter([1, 2, 3].values()); - /// let copy = Queue.clone(original); - /// Queue.clear(original); - /// assert Queue.size(original) == 0; - /// assert Queue.size(copy) == 3; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func clone(self : Queue) : Queue { - let copy = empty(); - for (element in values(self)) { - pushBack(copy, element) - }; - copy - }; - - /// Returns the number of elements in the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter(["A", "B", "C"].values()); - /// assert Queue.size(queue) == 3; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func size(self : Queue) : Nat { - self.size - }; - - /// Returns `true` if the queue contains no elements. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func isEmpty(self : Queue) : Bool { - self.size == 0 - }; - - /// Checks if an element exists in the queue using the provided equality function. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.contains(queue, Nat.equal, 2); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func contains(self : Queue, equal : (implicit : (T, T) -> Bool), element : T) : Bool { - for (existing in values(self)) { - if (equal(existing, element)) { - return true - } - }; - false - }; - - /// Returns the first element in the queue without removing it. - /// Returns null if the queue is empty. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.peekFront(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func peekFront(self : Queue) : ?T { - switch (self.front) { - case null null; - case (?node) ?node.value - } - }; - - /// Returns the last element in the queue without removing it. - /// Returns null if the queue is empty. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.peekBack(queue) == ?3; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func peekBack(self : Queue) : ?T { - switch (self.back) { - case null null; - case (?node) ?node.value - } - }; - - /// Adds an element to the front of the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.empty(); - /// Queue.pushFront(queue, 1); - /// assert Queue.peekFront(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func pushFront(self : Queue, element : T) { - let node : Node = { - value = element; - var next = self.front; - var previous = null - }; - switch (self.front) { - case null {}; - case (?first) first.previous := ?node - }; - self.front := ?node; - switch (self.back) { - case null self.back := ?node; - case (?_) {} - }; - self.size += 1 - }; - - /// Adds an element to the back of the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.empty(); - /// Queue.pushBack(queue, 1); - /// assert Queue.peekBack(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func pushBack(self : Queue, element : T) { - let node : Node = { - value = element; - var next = null; - var previous = self.back - }; - switch (self.back) { - case null {}; - case (?last) last.next := ?node - }; - self.back := ?node; - switch (self.front) { - case null self.front := ?node; - case (?_) {} - }; - self.size += 1 - }; - - /// Removes and returns the first element in the queue. - /// Returns null if the queue is empty. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.popFront(queue) == ?1; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func popFront(self : Queue) : ?T { - switch (self.front) { - case null null; - case (?first) { - self.front := first.next; - switch (self.front) { - case null { self.back := null }; - case (?newFirst) { newFirst.previous := null } - }; - self.size -= 1; - ?first.value - } - } - }; - - /// Removes and returns the last element in the queue. - /// Returns null if the queue is empty. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.popBack(queue) == ?3; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func popBack(self : Queue) : ?T { - switch (self.back) { - case null null; - case (?last) { - self.back := last.previous; - switch (self.back) { - case null { self.front := null }; - case (?newLast) { newLast.next := null } - }; - self.size -= 1; - ?last.value - } - } - }; - - /// Creates a new queue from an iterator. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter(["A", "B", "C"].values()); - /// assert Queue.size(queue) == 3; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func fromIter(iter : Iter.Iter) : Queue { - let queue = empty(); - for (element in iter) { - pushBack(queue, element) - }; - queue - }; - - /// Converts an iterator to a queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// transient let iter = ["A", "B", "C"].values(); - /// - /// let queue = iter.toQueue(); - /// - /// assert Queue.size(queue) == 3; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func toQueue(self : Iter.Iter) : Queue { - fromIter(self) - }; - - /// Creates a new queue from an array. - /// Elements appear in the same order as in the array. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromArray(["A", "B", "C"]); - /// assert Queue.size(queue) == 3; - /// assert Queue.peekFront(queue) == ?"A"; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the array. - public func fromArray(array : [T]) : Queue { - let queue = empty(); - for (element in array.vals()) { - pushBack(queue, element) - }; - queue - }; - - public func fromVarArray(array : [var T]) : Queue { - fromIter(array.values()) - }; - - /// Creates a new immutable array containing all elements from the queue. - /// Elements appear in the same order as in the queue (front to back). - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Array "mo:core/Array"; - /// - /// persistent actor { - /// let queue = Queue.fromArray(["A", "B", "C"]); - /// let array = Queue.toArray(queue); - /// assert array == ["A", "B", "C"]; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func toArray(self : Queue) : [T] { - let iter = values(self); - Array.tabulate( - self.size, - func(i) { - switch (iter.next()) { - case null { Prim.trap("Queue.toArray(): unexpected end of iterator") }; - case (?value) { value } - } - } - ) - }; - - public func toVarArray(self : Queue) : [var T] { - Array.toVarArray(toArray(self)) - }; - - /// Returns an iterator over the elements in the queue. - /// Iterates from front to back. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// persistent actor { - /// let queue = Queue.fromIter(["A", "B", "C"].values()); - /// transient let iter = Queue.values(queue); - /// assert iter.next() == ?"A"; - /// assert iter.next() == ?"B"; - /// assert iter.next() == ?"C"; - /// assert iter.next() == null; - /// } - /// ``` - /// - /// Runtime: O(1) for iterator creation, O(n) for full iteration - /// Space: O(1) - public func values(self : Queue) : Iter.Iter { - object { - var current = self.front; - - public func next() : ?T { - switch (current) { - case null null; - case (?node) { - current := node.next; - ?node.value - } - } - } - } - }; - - public func reverseValues(self : Queue) : Iter.Iter { - Iter.reverse(values(self)) - }; - - /// Tests whether all elements in the queue satisfy the given predicate. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([2, 4, 6].values()); - /// assert Queue.all(queue, func(x) { x % 2 == 0 }); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - public func all(self : Queue, predicate : T -> Bool) : Bool { - for (element in values(self)) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Tests whether any element in the queue satisfies the given predicate. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.any(queue, func (x) { x > 2 }); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func any(self : Queue, predicate : T -> Bool) : Bool { - for (element in values(self)) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Applies the given operation to all elements in the queue. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// var sum = 0; - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// Queue.forEach(queue, func(x) { sum += x }); - /// assert sum == 6; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func forEach(self : Queue, operation : T -> ()) { - for (element in values(self)) { - operation(element) - } - }; - - /// Creates a new queue by applying the given function to all elements. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let doubled = Queue.map(queue, func(x) { x * 2 }); - /// assert Queue.peekFront(doubled) == ?2; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func map(self : Queue, project : T -> U) : Queue { - let result = empty(); - for (element in values(self)) { - pushBack(result, project(element)) - }; - result - }; - - /// Creates a new queue containing only elements that satisfy the given predicate. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3, 4].values()); - /// let evens = Queue.filter(queue, func(x) { x % 2 == 0 }); - /// assert Queue.size(evens) == 2; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func filter(self : Queue, criterion : T -> Bool) : Queue { - let result = empty(); - for (element in values(self)) { - if (criterion(element)) { - pushBack(result, element) - } - }; - result - }; - - /// Creates a new queue by applying the given function to all elements - /// and keeping only the non-null results. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3, 4].values()); - /// let evenDoubled = Queue.filterMap( - /// queue, - /// func(x) { - /// if (x % 2 == 0) { ?(x * 2) } else { null } - /// } - /// ); - /// assert Queue.size(evenDoubled) == 2; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func filterMap(self : Queue, project : T -> ?U) : Queue { - let result = empty(); - for (element in values(self)) { - switch (project(element)) { - case null {}; - case (?newElement) pushBack(result, newElement) - } - }; - result - }; - - /// Compares two queues for equality using the provided equality function. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2, 3].values()); - /// let queue2 = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.equal(queue1, queue2, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func equal(self : Queue, other : Queue, equal : (implicit : (T, T) -> Bool)) : Bool { - if (size(self) != size(other)) { - return false - }; - let iterator1 = values(self); - let iterator2 = values(other); - loop { - let element1 = iterator1.next(); - let element2 = iterator2.next(); - switch (element1, element2) { - case (null, null) { - return true - }; - case (?element1, ?element2) { - if (not equal(element1, element2)) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Converts a queue to its string representation using the provided element formatter. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.toText(queue, Nat.toText) == "Queue[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// `n` denotes the number of elements stored in the queue. - public func toText(self : Queue, format : (implicit : (toText : T -> Text))) : Text { - var text = "Queue["; - var sep = ""; - for (element in values(self)) { - text #= sep # format(element); - sep := ", " - }; - text #= "]"; - text - }; - - /// Compares two queues using the provided comparison function. - /// Returns #less, #equal, or #greater. - /// - /// Example: - /// ```motoko - /// import Queue "mo:core/Queue"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2].values()); - /// let queue2 = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.compare(queue1, queue2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// `n` denotes the number of elements stored in the queue. - public func compare(self : Queue, other : Queue, compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let iterator1 = values(self); - let iterator2 = values(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?element1, ?element2) { - let comparison = compare(element1, element2); - if (comparison != #equal) { - return comparison - } - } - } - } - } -} diff --git a/.mops/core@2.5.0/src/Random.mo b/.mops/core@2.5.0/src/Random.mo deleted file mode 100644 index 4283653..0000000 --- a/.mops/core@2.5.0/src/Random.mo +++ /dev/null @@ -1,456 +0,0 @@ -/// Random number generation. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Random "mo:core/Random"; -/// ``` - -import Nat8 "Nat8"; -import Nat64 "Nat64"; -import Int "Int"; -import Nat "Nat"; -import Blob "Blob"; -import Runtime "Runtime"; - -module { - - public type State = { - var bytes : [Nat8]; - var index : Nat; - var bits : Nat8; - var bitMask : Nat8 - }; - - public type SeedState = { - random : State; - prng : PRNG.State - }; - - let rawRand = (actor "aaaaa-aa" : actor { raw_rand : () -> async Blob }).raw_rand; - - public let blob : shared () -> async Blob = rawRand; - - public func bool() : async Bool { - await* crypto().bool() - }; - public func nat8() : async Nat8 { - await* crypto().nat8() - }; - public func nat64() : async Nat64 { - await* crypto().nat64() - }; - public func nat64Range(fromInclusive : Nat64, toExclusive : Nat64) : async Nat64 { - await* crypto().nat64Range(fromInclusive, toExclusive) - }; - public func natRange(fromInclusive : Nat, toExclusive : Nat) : async Nat { - await* crypto().natRange(fromInclusive, toExclusive) - }; - public func intRange(fromInclusive : Int, toExclusive : Int) : async Int { - await* crypto().intRange(fromInclusive, toExclusive) - }; - - /// Initializes a random number generator state. This is used - /// to create a `Random` or `AsyncRandom` instance with a specific state. - /// The state is empty, but it can be reused after upgrading the canister. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// let state = Random.emptyState(); - /// transient let random = Random.cryptoFromState(state); - /// - /// public func main() : async () { - /// let coin = await* random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func emptyState() : State = { - var bytes = []; - var index = 0; - var bits = 0x00; - var bitMask = 0x00 - }; - - /// Initializes a pseudo-random number generator state with a 64-bit seed. - /// This is used to create a `Random` instance with a specific seed. - /// The seed is used to initialize the PRNG state. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// let state = Random.seedState(123); - /// transient let random = Random.seedFromState(state); - /// - /// public func main() : async () { - /// let coin = random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func seedState(seed : Nat64) : SeedState = { - random = emptyState(); - prng = PRNG.init(seed) - }; - - /// Creates a pseudo-random number generator from a 64-bit seed. - /// The seed is used to initialize the PRNG state. - /// This is suitable for simulations and testing, but not for cryptographic purposes. - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(123); - /// let coin = random.bool(); // true or false - /// ``` - /// @deprecated M0235 - public func seed(seed : Nat64) : Random { - seedFromState(seedState(seed)) - }; - - /// Creates a pseudo-random number generator with the given state. - /// This provides statistical randomness suitable for simulations and testing, - /// but should not be used for cryptographic purposes. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// let state = Random.seedState(123); - /// transient let random = Random.seedFromState(state); - /// - /// public func main() : async () { - /// let coin = random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func seedFromState(state : SeedState) : Random { - Random( - state.random, - func() : Blob { - // Generate 8 bytes directly from a single 64-bit number - let n = PRNG.next(state.prng); - let (b7, b6, b5, b4, b3, b2, b1, b0) = Nat64.explode(n); - Blob.fromArray([b0, b1, b2, b3, b4, b5, b6, b7]) - } - ) - }; - - /// Initializes a cryptographic random number generator - /// using entropy from the ICP management canister. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// transient let random = Random.crypto(); - /// - /// public func main() : async () { - /// let coin = await* random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func crypto() : AsyncRandom { - cryptoFromState(emptyState()) - }; - - /// Creates a random number generator suitable for cryptography - /// using entropy from the ICP management canister. Initializing - /// from a state makes it possible to reuse entropy after - /// upgrading the canister. - /// - /// Example: - /// ```motoko - /// import Random "mo:core/Random"; - /// - /// persistent actor { - /// let state = Random.emptyState(); - /// transient let random = Random.cryptoFromState(state); - /// - /// func example() : async () { - /// let coin = await* random.bool(); // true or false - /// } - /// } - /// ``` - /// @deprecated M0235 - public func cryptoFromState(state : State) : AsyncRandom { - AsyncRandom(state, func() : async* Blob { await rawRand() }) - }; - - /// @deprecated M0235 - public class Random(state : State, generator : () -> Blob) { - - func nextBit() : Bool { - if (0 : Nat8 == state.bitMask) { - state.bits := nat8(); - state.bitMask := 0x40; - 0 : Nat8 != state.bits & (0x80 : Nat8) - } else { - let m = state.bitMask; - state.bitMask >>= (1 : Nat8); - 0 : Nat8 != state.bits & m - } - }; - - /// Random choice between `true` and `false`. - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let coin = random.bool(); // true or false - /// ``` - /// @deprecated M0235 - public func bool() : Bool { - nextBit() - }; - - /// Random `Nat8` value in the range [0, 256). - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let byte = random.nat8(); // 0 to 255 - /// ``` - /// @deprecated M0235 - public func nat8() : Nat8 { - if (state.index >= state.bytes.size()) { - let newBytes = Blob.toArray(generator()); - if (newBytes.size() == 0) { - Runtime.trap("Random: generator produced empty Blob") - }; - state.bytes := newBytes; - state.index := 0 - }; - let byte = state.bytes[state.index]; - state.index += 1; - byte - }; - - // Helper function which returns a uniformly sampled `Nat64` in the range `[0, max]`. - // Uses rejection sampling to ensure uniform distribution even when the range - // doesn't divide evenly into 2^64. This avoids modulo bias that would occur - // from simply taking the modulo of a random 64-bit number. - func uniform64(max : Nat64) : Nat64 { - if (max == 0) { - return 0 - }; - // if (max == 1) { - // return switch (bool()) { - // case false 0; - // case true 1 - // } - // }; - if (max == Nat64.maxValue) { - return nat64() - }; - let toExclusive = max + 1; - // 2^64 - (2^64 % toExclusive) = (2^64-1) - (2^64-1 % toExclusive): - let cutoff = Nat64.maxValue - (Nat64.maxValue % toExclusive); - // 2^64 / toExclusive, with toExclusive > 1: - let multiple = Nat64.fromNat(/* 2^64 */ 0x10000000000000000 / Nat64.toNat(toExclusive)); - loop { - // Build up a random Nat64 from bytes - var number = nat64(); - // If number is below cutoff, we can use it - if (number < cutoff) { - // Scale down to desired range - return number / multiple - }; - // Otherwise reject and try again - } - }; - - /// Random `Nat64` value in the range [0, 2^64). - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let number = random.nat64(); // 0 to 18446744073709551615 - /// ``` - /// @deprecated M0235 - public func nat64() : Nat64 { - (Nat64.fromNat(Nat8.toNat(nat8())) << 56) | (Nat64.fromNat(Nat8.toNat(nat8())) << 48) | (Nat64.fromNat(Nat8.toNat(nat8())) << 40) | (Nat64.fromNat(Nat8.toNat(nat8())) << 32) | (Nat64.fromNat(Nat8.toNat(nat8())) << 24) | (Nat64.fromNat(Nat8.toNat(nat8())) << 16) | (Nat64.fromNat(Nat8.toNat(nat8())) << 8) | Nat64.fromNat(Nat8.toNat(nat8())) - }; - - /// Random `Nat64` value in the range [fromInclusive, toExclusive). - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let dice = random.nat64Range(1, 7); // 1 to 6 - /// ``` - /// @deprecated M0235 - public func nat64Range(fromInclusive : Nat64, toExclusive : Nat64) : Nat64 { - if (fromInclusive >= toExclusive) { - Runtime.trap("Random.nat64Range(): fromInclusive >= toExclusive") - }; - uniform64(toExclusive - fromInclusive - 1) + fromInclusive - }; - - /// Random `Nat` value in the range [fromInclusive, toExclusive). - /// - /// Example: - /// ```motoko include=import - /// let random = Random.seed(42); - /// let index = random.natRange(0, 10); // 0 to 9 - /// ``` - /// @deprecated M0235 - public func natRange(fromInclusive : Nat, toExclusive : Nat) : Nat { - if (fromInclusive >= toExclusive) { - Runtime.trap("Random.natRange(): fromInclusive >= toExclusive") - }; - Nat64.toNat(uniform64(Nat64.fromNat(toExclusive - fromInclusive - 1))) + fromInclusive - }; - - /// @deprecated M0235 - public func intRange(fromInclusive : Int, toExclusive : Int) : Int { - let range = Nat.fromInt(toExclusive - fromInclusive - 1); - Nat64.toNat(uniform64(Nat64.fromNat(range))) + fromInclusive - }; - - }; - - /// @deprecated M0235 - public class AsyncRandom(state : State, generator : () -> async* Blob) { - - func nextBit() : async* Bool { - if (0 : Nat8 == state.bitMask) { - state.bits := await* nat8(); - state.bitMask := 0x40; - 0 : Nat8 != state.bits & (0x80 : Nat8) - } else { - let m = state.bitMask; - state.bitMask >>= (1 : Nat8); - 0 : Nat8 != state.bits & m - } - }; - - /// Random choice between `true` and `false`. - /// @deprecated M0235 - public func bool() : async* Bool { - await* nextBit() - }; - - /// Random `Nat8` value in the range [0, 256). - /// @deprecated M0235 - public func nat8() : async* Nat8 { - if (state.index >= state.bytes.size()) { - let newBytes = Blob.toArray(await* generator()); - if (newBytes.size() == 0) { - Runtime.trap("AsyncRandom: generator produced empty Blob") - }; - state.bytes := newBytes; - state.index := 0 - }; - let byte = state.bytes[state.index]; - state.index += 1; - byte - }; - - // Helper function which returns a uniformly sampled `Nat64` in the range `[0, max]`. - // Uses rejection sampling to ensure uniform distribution even when the range - // doesn't divide evenly into 2^64. This avoids modulo bias that would occur - // from simply taking the modulo of a random 64-bit number. - func uniform64(max : Nat64) : async* Nat64 { - if (max == 0) { - return 0 - }; - if (max == Nat64.maxValue) { - return await* nat64() - }; - let toExclusive = max + 1; - // 2^64 - (2^64 % toExclusive) = (2^64-1) - (2^64-1 % toExclusive): - let cutoff = Nat64.maxValue - (Nat64.maxValue % toExclusive); - // 2^64 / toExclusive, with toExclusive > 1: - let multiple = Nat64.fromNat(/* 2^64 */ 0x10000000000000000 / Nat64.toNat(toExclusive)); - loop { - // Build up a random Nat64 from bytes - var number = await* nat64(); - // If number is below cutoff, we can use it - if (number < cutoff) { - // Scale down to desired range - return number / multiple - }; - // Otherwise reject and try again - } - }; - - /// Random `Nat64` value in the range [0, 2^64). - /// @deprecated M0235 - public func nat64() : async* Nat64 { - (Nat64.fromNat(Nat8.toNat(await* nat8())) << 56) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 48) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 40) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 32) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 24) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 16) | (Nat64.fromNat(Nat8.toNat(await* nat8())) << 8) | Nat64.fromNat(Nat8.toNat(await* nat8())) - }; - - /// Random `Nat64` value in the range [fromInclusive, toExclusive). - /// @deprecated M0235 - public func nat64Range(fromInclusive : Nat64, toExclusive : Nat64) : async* Nat64 { - if (fromInclusive >= toExclusive) { - Runtime.trap("AsyncRandom.nat64Range(): fromInclusive >= toExclusive") - }; - (await* uniform64(toExclusive - fromInclusive - 1)) + fromInclusive - }; - - /// Random `Nat` value in the range [fromInclusive, toExclusive). - /// @deprecated M0235 - public func natRange(fromInclusive : Nat, toExclusive : Nat) : async* Nat { - if (fromInclusive >= toExclusive) { - Runtime.trap("AsyncRandom.natRange(): fromInclusive >= toExclusive") - }; - Nat64.toNat(await* uniform64(Nat64.fromNat(toExclusive - fromInclusive - 1))) + fromInclusive - }; - - /// Random `Int` value in the range [fromInclusive, toExclusive). - /// @deprecated M0235 - public func intRange(fromInclusive : Int, toExclusive : Int) : async* Int { - let range = Nat.fromInt(toExclusive - fromInclusive - 1); - Nat64.toNat(await* uniform64(Nat64.fromNat(range))) + fromInclusive - }; - - }; - - // Derived from https://github.com/research-ag/prng - module PRNG { - let p : Nat64 = 24; - let q : Nat64 = 11; - let r : Nat64 = 3; - - public type State = { - var a : Nat64; - var b : Nat64; - var c : Nat64; - var d : Nat64 - }; - - public func init(seed : Nat64) : State { - init3(seed, seed, seed) - }; - - public func init3(seed1 : Nat64, seed2 : Nat64, seed3 : Nat64) : State { - let state : State = { - var a = seed1; - var b = seed2; - var c = seed3; - var d = 1 - }; - for (_ in Nat.range(0, 11)) ignore next(state); - state - }; - - public func next(state : State) : Nat64 { - let tmp = state.a +% state.b +% state.d; - state.a := state.b ^ (state.b >> q); - state.b := state.c +% (state.c << r); - state.c := (state.c <<> p) +% tmp; - state.d +%= 1; - tmp - } - } - -} diff --git a/.mops/core@2.5.0/src/Region.mo b/.mops/core@2.5.0/src/Region.mo deleted file mode 100644 index a08783a..0000000 --- a/.mops/core@2.5.0/src/Region.mo +++ /dev/null @@ -1,485 +0,0 @@ -/// Byte-level access to isolated, virtual stable memory regions. -/// -/// This is a moderately lightweight abstraction over IC _stable memory_ and supports persisting -/// regions of binary data across Motoko upgrades. -/// Use of this module is fully compatible with Motoko's use of -/// _stable variables_, whose persistence mechanism also uses (real) IC stable memory internally, but does not interfere with this API. -/// It is also fully compatible with existing uses of the `ExperimentalStableMemory` library, which has a similar interface, but, -/// only supported a single memory region, without isolation between different applications. -/// -/// The `Region` type is stable and can be used in stable data structures. -/// -/// A new, empty `Region` is allocated using function `new()`. -/// -/// Regions are stateful objects and can be distinguished by the numeric identifier returned by function `id(region)`. -/// Every region owns an initially empty, but growable sequence of virtual IC stable memory pages. -/// The current size, in pages, of a region is returned by function `size(region)`. -/// The size of a region determines the range, [ 0, ..., size(region)*2^16 ), of valid byte-offsets into the region; these offsets are used as the source and destination of `load`/`store` operations on the region. -/// -/// Memory is allocated to a region, using function `grow(region, pages)`, sequentially and on demand, in units of 64KiB logical pages, starting with 0 allocated pages. -/// A call to `grow` may succeed, returning the previous size of the region, or fail, returning a sentinel value. New pages are zero initialized. -/// -/// A size of a region can only grow and never shrink. -/// In addition, the stable memory pages allocated to a region will *not* be reclaimed by garbage collection, even -/// if the region object itself becomes unreachable. -/// -/// Growth is capped by a soft limit on physical page count controlled by compile-time flag -/// `--max-stable-pages ` (the default is 65536, or 4GiB). -/// -/// Each `load` operation loads from region relative byte address `offset` in little-endian -/// format using the natural bit-width of the type in question. -/// The operation traps if attempting to read beyond the current region size. -/// -/// Each `store` operation stores to region relative byte address `offset` in little-endian format using the natural bit-width of the type in question. -/// The operation traps if attempting to write beyond the current region size. -/// -/// Text values can be handled by using `Text.decodeUtf8` and `Text.encodeUtf8`, in conjunction with `loadBlob` and `storeBlob`. -/// -/// The current region allocation and region contents are preserved across upgrades. -/// -/// NB: The IC's actual stable memory size (`ic0.stable_size`) may exceed the -/// total page size reported by summing all regions sizes. -/// This (and the cap on growth) are to accommodate Motoko's stable variables and bookkeeping for regions. -/// Applications that plan to use Motoko stable variables sparingly or not at all can -/// increase `--max-stable-pages` as desired, approaching the IC maximum (initially 8GiB, then 32Gib, currently 64Gib). -/// All applications should reserve at least one page for stable variable data, even when no stable variables are used. -/// -/// Usage: -/// ```motoko no-repl name=import -/// import Region "mo:core/Region"; -/// ``` - -import Prim "mo:⛔"; - -module { - - /// A stateful handle to an isolated region of IC stable memory. - /// `Region` is a stable type and regions can be stored in stable variables. - /// @deprecated M0235 - public type Region = Prim.Types.Region; - - /// Allocate a new, isolated Region of size 0. - /// - /// Example: - /// - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// assert Region.size(region) == 0; - /// } - /// } - /// ``` - public let new : () -> Region = Prim.regionNew; - - /// Return a Nat identifying the given region. - /// May be used for equality, comparison and hashing. - /// NB: Regions returned by `new()` are numbered from 16 - /// (regions 0..15 are currently reserved for internal use). - /// Allocate a new, isolated Region of size 0. - /// - /// Example: - /// - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// assert Region.id(region) == 16; - /// } - /// } - /// ``` - public let id : (self : Region) -> Nat = Prim.regionId; - - /// Current size of `region`, in pages. - /// Each page is 64KiB (65536 bytes). - /// Initially `0`. - /// Preserved across upgrades, together with contents of allocated - /// stable memory. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let beforeSize = Region.size(region); - /// ignore Region.grow(region, 10); - /// let afterSize = Region.size(region); - /// assert afterSize - beforeSize == 10; - /// } - /// } - /// ``` - public let size : (self : Region) -> (pages : Nat64) = Prim.regionSize; - - /// Grow current `size` of `region` by the given number of pages. - /// Each page is 64KiB (65536 bytes). - /// Returns the previous `size` when able to grow. - /// Returns `0xFFFF_FFFF_FFFF_FFFF` if remaining pages insufficient. - /// Every new page is zero-initialized, containing byte 0x00 at every offset. - /// Function `grow` is capped by a soft limit on `size` controlled by compile-time flag - /// `--max-stable-pages ` (the default is 65536, or 4GiB). - /// - /// Example: - /// ```motoko no-repl include=import - /// import Error "mo:core/Error"; - /// - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let beforeSize = Region.grow(region, 10); - /// if (beforeSize == 0xFFFF_FFFF_FFFF_FFFF) { - /// throw Error.reject("Out of memory"); - /// }; - /// let afterSize = Region.size(region); - /// assert afterSize - beforeSize == 10; - /// } - /// } - /// ``` - public let grow : (self : Region, newPages : Nat64) -> (oldPages : Nat64) = Prim.regionGrow; - - /// Within `region`, load a `Nat8` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat8 = 123; - /// Region.storeNat8(region, offset, value); - /// assert Region.loadNat8(region, offset) == 123; - /// } - /// } - /// ``` - public let loadNat8 : (self : Region, offset : Nat64) -> Nat8 = Prim.regionLoadNat8; - - /// Within `region`, store a `Nat8` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat8 = 123; - /// Region.storeNat8(region, offset, value); - /// assert Region.loadNat8(region, offset) == 123; - /// } - /// } - /// ``` - public let storeNat8 : (self : Region, offset : Nat64, value : Nat8) -> () = Prim.regionStoreNat8; - - /// Within `region`, load a `Nat16` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat16 = 123; - /// Region.storeNat16(region, offset, value); - /// assert Region.loadNat16(region, offset) == 123; - /// } - /// } - /// ``` - public let loadNat16 : (self : Region, offset : Nat64) -> Nat16 = Prim.regionLoadNat16; - - /// Within `region`, store a `Nat16` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat16 = 123; - /// Region.storeNat16(region, offset, value); - /// assert Region.loadNat16(region, offset) == 123; - /// } - /// } - /// ``` - public let storeNat16 : (self : Region, offset : Nat64, value : Nat16) -> () = Prim.regionStoreNat16; - - /// Within `region`, load a `Nat32` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat32 = 123; - /// Region.storeNat32(region, offset, value); - /// assert Region.loadNat32(region, offset) == 123; - /// } - /// } - /// ``` - public let loadNat32 : (self : Region, offset : Nat64) -> Nat32 = Prim.regionLoadNat32; - - /// Within `region`, store a `Nat32` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat32 = 123; - /// Region.storeNat32(region, offset, value); - /// assert Region.loadNat32(region, offset) == 123; - /// } - /// } - /// ``` - public func storeNat32(self : Region, offset : Nat64, value : Nat32) : () = Prim.regionStoreNat32(self, offset, value); - - /// Within `region`, load a `Nat64` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat64 = 123; - /// Region.storeNat64(region, offset, value); - /// assert Region.loadNat64(region, offset) == 123; - /// } - /// } - /// ``` - public let loadNat64 : (self : Region, offset : Nat64) -> Nat64 = Prim.regionLoadNat64; - - /// Within `region`, store a `Nat64` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Nat64 = 123; - /// Region.storeNat64(region, offset, value); - /// assert Region.loadNat64(region, offset) == 123; - /// } - /// } - /// ``` - public let storeNat64 : (self : Region, offset : Nat64, value : Nat64) -> () = Prim.regionStoreNat64; - - /// Within `region`, load a `Int8` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int8 = 123; - /// Region.storeInt8(region, offset, value); - /// assert Region.loadInt8(region, offset) == 123; - /// } - /// } - /// ``` - public let loadInt8 : (self : Region, offset : Nat64) -> Int8 = Prim.regionLoadInt8; - - /// Within `region`, store a `Int8` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int8 = 123; - /// Region.storeInt8(region, offset, value); - /// assert Region.loadInt8(region, offset) == 123; - /// } - /// } - /// ``` - public let storeInt8 : (self : Region, offset : Nat64, value : Int8) -> () = Prim.regionStoreInt8; - - /// Within `region`, load a `Int16` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int16 = 123; - /// Region.storeInt16(region, offset, value); - /// assert Region.loadInt16(region, offset) == 123; - /// } - /// } - /// ``` - public let loadInt16 : (self : Region, offset : Nat64) -> Int16 = Prim.regionLoadInt16; - - /// Within `region`, store a `Int16` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int16 = 123; - /// Region.storeInt16(region, offset, value); - /// assert Region.loadInt16(region, offset) == 123; - /// } - /// } - /// ``` - public let storeInt16 : (self : Region, offset : Nat64, value : Int16) -> () = Prim.regionStoreInt16; - - /// Within `region`, load a `Int32` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int32 = 123; - /// Region.storeInt32(region, offset, value); - /// assert Region.loadInt32(region, offset) == 123; - /// } - /// } - /// ``` - public let loadInt32 : (self : Region, offset : Nat64) -> Int32 = Prim.regionLoadInt32; - - /// Within `region`, store a `Int32` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int32 = 123; - /// Region.storeInt32(region, offset, value); - /// assert Region.loadInt32(region, offset) == 123; - /// } - /// } - /// ``` - public let storeInt32 : (self : Region, offset : Nat64, value : Int32) -> () = Prim.regionStoreInt32; - - /// Within `region`, load a `Int64` value from `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int64 = 123; - /// Region.storeInt64(region, offset, value); - /// assert Region.loadInt64(region, offset) == 123; - /// } - /// } - /// ``` - public let loadInt64 : (self : Region, offset : Nat64) -> Int64 = Prim.regionLoadInt64; - - /// Within `region`, store a `Int64` value at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value : Int64 = 123; - /// Region.storeInt64(region, offset, value); - /// assert Region.loadInt64(region, offset) == 123; - /// } - /// } - /// ``` - public let storeInt64 : (self : Region, offset : Nat64, value : Int64) -> () = Prim.regionStoreInt64; - - /// Within `region`, loads a `Float` value from the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value = 1.25; - /// Region.storeFloat(region, offset, value); - /// assert Region.loadFloat(region, offset) == 1.25; - /// } - /// } - /// ``` - public let loadFloat : (self : Region, offset : Nat64) -> Float = Prim.regionLoadFloat; - - /// Within `region`, store float `value` at the given `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value = 1.25; - /// Region.storeFloat(region, offset, value); - /// assert Region.loadFloat(region, offset) == 1.25; - /// } - /// } - /// ``` - public let storeFloat : (self : Region, offset : Nat64, value : Float) -> () = Prim.regionStoreFloat; - - /// Within `region,` load `size` bytes starting from `offset` as a `Blob`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// import Blob "mo:core/Blob"; - /// - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value = Blob.fromArray([1, 2, 3]); - /// let size = value.size(); - /// Region.storeBlob(region, offset, value); - /// assert Blob.toArray(Region.loadBlob(region, offset, size)) == [1, 2, 3]; - /// } - /// } - /// ``` - public let loadBlob : (self : Region, offset : Nat64, size : Nat) -> Blob = Prim.regionLoadBlob; - - /// Within `region, write `blob.size()` bytes of `blob` beginning at `offset`. - /// Traps on an out-of-bounds access. - /// - /// Example: - /// ```motoko no-repl include=import - /// import Blob "mo:core/Blob"; - /// - /// persistent actor { - /// public func example() : async () { - /// let region = Region.new(); - /// let offset : Nat64 = 0; - /// let value = Blob.fromArray([1, 2, 3]); - /// let size = value.size(); - /// Region.storeBlob(region, offset, value); - /// assert Blob.toArray(Region.loadBlob(region, offset, size)) == [1, 2, 3]; - /// } - /// } - /// ``` - public let storeBlob : (self : Region, offset : Nat64, value : Blob) -> () = Prim.regionStoreBlob; - -} diff --git a/.mops/core@2.5.0/src/Result.mo b/.mops/core@2.5.0/src/Result.mo deleted file mode 100644 index 08aa478..0000000 --- a/.mops/core@2.5.0/src/Result.mo +++ /dev/null @@ -1,355 +0,0 @@ -/// Module for error handling with the Result type. -/// -/// The Result type is used for returning and propagating errors. It has two variants: -/// `#ok(Ok)`, representing success and containing a value, and `#err(Err)`, representing -/// error and containing an error value. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import Result "mo:core/Result"; -/// ``` - -import Order "Order"; -import Types "Types"; - -module { - - /// The Result type used for returning and propagating errors. - /// - /// The simplest way of working with Results is to pattern match on them. - /// For example: - /// ```motoko include=import - /// import Text "mo:core/Text"; - /// - /// type Email = Text; - /// type ErrorMessage = Text; - /// - /// func validateEmail(email : Text) : Result.Result { - /// let parts = Text.split(email, #char '@'); - /// let beforeAt = parts.next(); - /// let afterAt = parts.next(); - /// switch (beforeAt, afterAt) { - /// case (?local, ?domain) { - /// if (local == "") return #err("Username cannot be empty"); - /// if (not Text.contains(domain, #char '.')) return #err("Invalid domain format"); - /// #ok(email) - /// }; - /// case _ #err("Email must contain exactly one @ symbol") - /// } - /// }; - /// - /// assert validateEmail("user@example.com") == #ok("user@example.com"); - /// assert validateEmail("invalid.email") == #err("Email must contain exactly one @ symbol"); - /// assert validateEmail("@domain.com") == #err("Username cannot be empty"); - /// assert validateEmail("user@invalid") == #err("Invalid domain format"); - /// ``` - /// @deprecated M0235 - public type Result = Types.Result; - - /// Compares two Results for equality. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let result1 = #ok 10; - /// let result2 = #ok 10; - /// let result3 = #err "error"; - /// - /// assert Result.equal(result1, result2, Nat.equal, Text.equal); - /// assert not Result.equal(result1, result3, Nat.equal, Text.equal); - /// ``` - public func equal( - self : Result, - other : Result, - equalOk : (implicit : (equal : Ok, Ok) -> Bool), - equalErr : (implicit : (equal : (Err, Err) -> Bool)) - ) : Bool { - switch (self, other) { - case (#ok(ok1), #ok(ok2)) { - equalOk(ok1, ok2) - }; - case (#err(err1), #err(err2)) { - equalErr(err1, err2) - }; - case _ { false } - } - }; - - /// Compares two Result values. `#ok` is larger than `#err`. This ordering is - /// arbitrary, but it lets you for example use Results as keys in ordered maps. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let result1 = #ok 5; - /// let result2 = #ok 10; - /// let result3 = #err "error"; - /// - /// assert Result.compare(result1, result2, Nat.compare, Text.compare) == #less; - /// assert Result.compare(result2, result1, Nat.compare, Text.compare) == #greater; - /// assert Result.compare(result1, result3, Nat.compare, Text.compare) == #greater; - /// ``` - public func compare( - self : Result, - other : Result, - compareOk : (implicit : (compare : (Ok, Ok) -> Order.Order)), - compareErr : (implicit : (compare : (Err, Err) -> Order.Order)) - ) : Order.Order { - switch (self, other) { - case (#ok(ok1), #ok(ok2)) { - compareOk(ok1, ok2) - }; - case (#err(err1), #err(err2)) { - compareErr(err1, err2) - }; - case (#ok(_), _) { #greater }; - case (#err(_), _) { #less } - } - }; - - /// Allows sequencing of Result values and functions that return - /// Results themselves. - /// ```motoko include=import - /// type Result = Result.Result; - /// func largerThan10(x : Nat) : Result = - /// if (x > 10) { #ok(x) } else { #err("Not larger than 10.") }; - /// - /// func smallerThan20(x : Nat) : Result = - /// if (x < 20) { #ok(x) } else { #err("Not smaller than 20.") }; - /// - /// func between10And20(x : Nat) : Result = - /// Result.chain(largerThan10(x), smallerThan20); - /// - /// assert between10And20(15) == #ok(15); - /// assert between10And20(9) == #err("Not larger than 10."); - /// assert between10And20(21) == #err("Not smaller than 20."); - /// ``` - public func chain( - self : Result, - f : Ok1 -> Result - ) : Result { - switch self { - case (#err(e)) { #err(e) }; - case (#ok(r)) { f(r) } - } - }; - - /// Flattens a nested Result. - /// - /// ```motoko include=import - /// assert Result.flatten(#ok(#ok(10))) == #ok(10); - /// assert Result.flatten(#err("Wrong")) == #err("Wrong"); - /// assert Result.flatten(#ok(#err("Wrong"))) == #err("Wrong"); - /// ``` - public func flatten( - self : Result, Err> - ) : Result { - switch self { - case (#ok(ok)) { ok }; - case (#err(err)) { #err(err) } - } - }; - - /// Maps the `Ok` type/value, leaving any `Err` type/value unchanged. - /// - /// Example: - /// ```motoko include=import - /// let result1 = #ok(42); - /// let result2 = #err("error"); - /// - /// let doubled1 = Result.mapOk(result1, func x = x * 2); - /// assert doubled1 == #ok(84); - /// - /// let doubled2 = Result.mapOk(result2, func x = x * 2); - /// assert doubled2 == #err("error"); - /// ``` - public func mapOk( - self : Result, - f : Ok1 -> Ok2 - ) : Result { - switch self { - case (#err(e)) { #err(e) }; - case (#ok(r)) { #ok(f(r)) } - } - }; - - /// Maps the `Err` type/value, leaving any `Ok` type/value unchanged. - /// - /// Example: - /// ```motoko include=import - /// let result1 = #ok(42); - /// let result2 = #err("error"); - /// - /// let mapped1 = Result.mapErr(result1, func x = x # "!"); - /// assert mapped1 == #ok(42); - /// - /// let mapped2 = Result.mapErr(result2, func x = x # "!"); - /// assert mapped2 == #err("error!"); - /// ``` - public func mapErr( - self : Result, - f : Err1 -> Err2 - ) : Result { - switch self { - case (#err(e)) { #err(f(e)) }; - case (#ok(r)) { #ok(r) } - } - }; - - /// Create a result from an option, including an error value to handle the `null` case. - /// ```motoko include=import - /// assert Result.fromOption(?42, "err") == #ok(42); - /// assert Result.fromOption(null, "err") == #err("err"); - /// ``` - public func fromOption(x : ?Ok, err : Err) : Result { - switch x { - case (?x) { #ok(x) }; - case null { #err(err) } - } - }; - - /// Create an option from a result, turning all #err into `null`. - /// ```motoko include=import - /// assert Result.toOption(#ok(42)) == ?42; - /// assert Result.toOption(#err("err")) == null; - /// ``` - public func toOption(self : Result) : ?Ok { - switch self { - case (#ok(x)) { ?x }; - case (#err(_)) { null } - } - }; - - /// Applies a function to a successful value and discards the result. Use - /// `forOk` if you're only interested in the side effect `f` produces. - /// - /// ```motoko include=import - /// var counter : Nat = 0; - /// Result.forOk(#ok(5), func (x : Nat) { counter += x }); - /// assert counter == 5; - /// Result.forOk(#err("Error"), func (x : Nat) { counter += x }); - /// assert counter == 5; - /// ``` - public func forOk(self : Result, f : Ok -> ()) { - switch self { - case (#ok(ok)) { f(ok) }; - case _ {} - } - }; - - /// Applies a function to an error value and discards the result. Use - /// `forErr` if you're only interested in the side effect `f` produces. - /// - /// ```motoko include=import - /// var counter : Nat = 0; - /// Result.forErr(#err("Error"), func (x : Text) { counter += 1 }); - /// assert counter == 1; - /// Result.forErr(#ok(5), func (x : Text) { counter += 1 }); - /// assert counter == 1; - /// ``` - public func forErr(self : Result, f : Err -> ()) { - switch self { - case (#err(err)) { f(err) }; - case _ {} - } - }; - - /// Whether this Result is an `#ok`. - /// - /// Example: - /// ```motoko include=import - /// assert Result.isOk(#ok(42)); - /// assert not Result.isOk(#err("error")); - /// ``` - public func isOk(self : Result) : Bool { - switch self { - case (#ok(_)) { true }; - case (#err(_)) { false } - } - }; - - /// Whether this Result is an `#err`. - /// - /// Example: - /// ```motoko include=import - /// assert Result.isErr(#err("error")); - /// assert not Result.isErr(#ok(42)); - /// ``` - public func isErr(self : Result) : Bool { - switch self { - case (#ok(_)) { false }; - case (#err(_)) { true } - } - }; - - /// Asserts that its argument is an `#ok` result, traps otherwise. - /// - /// Example: - /// ```motoko include=import - /// Result.assertOk(#ok(42)); // succeeds - /// // Result.assertOk(#err("error")); // would trap - /// ``` - public func assertOk(self : Result) { - switch self { - case (#err(_)) { assert false }; - case (#ok(_)) {} - } - }; - - /// Asserts that its argument is an `#err` result, traps otherwise. - /// - /// Example: - /// ```motoko include=import - /// Result.assertErr(#err("error")); // succeeds - /// // Result.assertErr(#ok(42)); // would trap - /// ``` - public func assertErr(self : Result) { - switch self { - case (#err(_)) {}; - case (#ok(_)) assert false - } - }; - - /// Converts an upper cased `#Ok`, `#Err` result type into a lowercased `#ok`, `#err` result type. - /// On the IC, a common convention is to use `#Ok` and `#Err` as the variants of a result type, - /// but in Motoko, we use `#ok` and `#err` instead. - /// - /// Example: - /// ```motoko include=import - /// let upper = #Ok(42); - /// let lower = Result.fromUpper(upper); - /// assert lower == #ok(42); - /// ``` - public func fromUpper( - result : { #Ok : Ok; #Err : Err } - ) : Result { - switch result { - case (#Ok(ok)) { #ok(ok) }; - case (#Err(err)) { #err(err) } - } - }; - - /// Converts a lower cased `#ok`, `#err` result type into an upper cased `#Ok`, `#Err` result type. - /// On the IC, a common convention is to use `#Ok` and `#Err` as the variants of a result type, - /// but in Motoko, we use `#ok` and `#err` instead. - /// - /// Example: - /// ```motoko include=import - /// let lower = #ok(42); - /// let upper = Result.toUpper(lower); - /// assert upper == #Ok(42); - /// ``` - public func toUpper( - self : Result - ) : { #Ok : Ok; #Err : Err } { - switch self { - case (#ok(ok)) { #Ok(ok) }; - case (#err(err)) { #Err(err) } - } - }; - -} diff --git a/.mops/core@2.5.0/src/Runtime.mo b/.mops/core@2.5.0/src/Runtime.mo deleted file mode 100644 index 4a797a1..0000000 --- a/.mops/core@2.5.0/src/Runtime.mo +++ /dev/null @@ -1,70 +0,0 @@ -/// Runtime utilities. -/// These functions were originally part of the `Debug` module. -/// -/// ```motoko name=import -/// import Runtime "mo:core/Runtime"; -/// ``` -import Prim "mo:⛔"; - -module { - - /// `trap(t)` traps execution with a user-provided diagnostic message. - /// - /// The caller of a future whose execution called `trap(t)` will - /// observe the trap as an `Error` value, thrown at `await`, with code - /// `#canister_error` and message `m`. Here `m` is a more descriptive `Text` - /// message derived from the provided `t`. See example for more details. - /// - /// NOTE: Other execution environments that cannot handle traps may only - /// propagate the trap and terminate execution, with or without some - /// descriptive message. - /// - /// ```motoko include=import no-validate - /// Runtime.trap("An error occurred!"); - /// ``` - public func trap(errorMessage : Text) : None { - Prim.trap errorMessage - }; - - /// `unreachable()` traps execution when code that should be unreachable is reached. - /// - /// This function is useful for marking code paths that should never be executed, - /// such as after exhaustive pattern matches or unreachable control flow branches. - /// If execution reaches this function, it indicates a programming error. - /// - /// ```motoko include=import no-validate - /// let number = switch (?5) { - /// case (?n) n; - /// case null Runtime.unreachable(); - /// }; - /// assert number == 5; - /// ``` - public func unreachable() : None { - trap("Runtime.unreachable()") - }; - - /// Returns the names of all canister environment variables. - /// - /// Example: - /// ```motoko include=import no-validate - /// let names = Runtime.envVarNames(); - /// ``` - public func envVarNames() : [Text] { - return Prim.envVarNames() - }; - - /// Returns an optional value of the canister environment variable with the given name. - /// - /// Example: - /// ```motoko include=import no-validate - /// let value = Runtime.envVar("MY_ENV_VAR"); - /// let result = switch (value) { - /// case (?v) v; - /// case null Runtime.trap("Unknown environment variable"); - /// }; - /// ``` - public func envVar(name : Text) : ?Text { - return Prim.envVar(name) - } - -} diff --git a/.mops/core@2.5.0/src/Set.mo b/.mops/core@2.5.0/src/Set.mo deleted file mode 100644 index 20ad4f5..0000000 --- a/.mops/core@2.5.0/src/Set.mo +++ /dev/null @@ -1,2756 +0,0 @@ -/// Imperative (mutable) sets based on order/comparison of elements. -/// A set is a collection of elements without duplicates. -/// The set data structure type is stable and can be used for orthogonal persistence. -/// -/// Example: -/// ```motoko -/// import Set "mo:core/Set"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// let set = Set.fromIter([3, 1, 2, 3].vals(), Nat.compare); -/// assert Set.size(set) == 3; -/// assert not Set.contains(set, Nat.compare, 4); -/// let diff = Set.difference(set, set, Nat.compare); -/// assert Set.isEmpty(diff); -/// } -/// ``` -/// -/// These sets are implemented as B-trees with order 32, a balanced search tree of ordered elements. -/// -/// Performance: -/// * Runtime: `O(log(n))` worst case cost per insertion, removal, and retrieval operation. -/// * Space: `O(n)` for storing the entire tree, -/// where `n` denotes the number of elements stored in the set. - -// Data structure implementation is courtesy of Byron Becker. -// Source: https://github.com/canscale/StableHeapBTreeMap -// Copyright (c) 2022 Byron Becker. -// Distributed under Apache 2.0 license. -// With adjustments by the Motoko team. - -import PureSet "pure/Set"; -import Types "Types"; -import Order "Order"; -import Array "Array"; -import VarArray "VarArray"; -import Runtime "Runtime"; -import Stack "Stack"; -import Option "Option"; -import Iter "Iter"; -import BTreeHelper "internal/BTreeHelper"; - -module { - let btreeOrder = 32; // Should be >= 4 and <= 512. - - public type Set = Types.Set.Set; - type Node = Types.Set.Node; - type Data = Types.Set.Data; - type Internal = Types.Set.Internal; - type Leaf = Types.Set.Leaf; - - /// Convert the mutable set to an immutable, purely functional set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import PureSet "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 1].values(), Nat.compare); - /// let pureSet = Set.toPure(set, Nat.compare); - /// assert Iter.toArray(PureSet.values(pureSet)) == Iter.toArray(Set.values(set)); - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - /// @deprecated M0235 - public func toPure(self : Set, compare : (implicit : (T, T) -> Order.Order)) : PureSet.Set { - PureSet.fromIter(values(self), compare) - }; - - /// Convert an immutable, purely functional set to a mutable set. - /// - /// Example: - /// ```motoko - /// import PureSet "mo:core/pure/Set"; - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let pureSet = PureSet.fromIter([3, 1, 2].values(), Nat.compare); - /// let set = Set.fromPure(pureSet, Nat.compare); - /// assert Iter.toArray(Set.values(set)) == Iter.toArray(PureSet.values(pureSet)); - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func fromPure(set : PureSet.Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - fromIter(PureSet.values(set), compare) - }; - - public func fromArray(array : [T], compare : (implicit : (T, T) -> Order.Order)) : Set { - fromIter(array.values(), compare) - }; - - /// Create a copy of the mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let originalSet = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let clonedSet = Set.clone(originalSet); - /// Set.add(originalSet, Nat.compare, 4); - /// assert Set.size(clonedSet) == 3; - /// assert Set.size(originalSet) == 4; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the set. - public func clone(self : Set) : Set { - { - var root = cloneNode(self.root); - var size = self.size - } - }; - - /// Create a new empty mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Set.size(set) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Set { - { - var root = #leaf({ - data = { - elements = VarArray.repeat(null, btreeOrder - 1); - var count = 0 - } - }); - var size = 0 - } - }; - - /// Create a new mutable set with a single element. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// - /// persistent actor { - /// let cities = Set.singleton("Zurich"); - /// assert Set.size(cities) == 1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func singleton(element : T) : Set { - let elements = VarArray.repeat(null, btreeOrder - 1); - elements[0] := ?element; - { - var root = - #leaf({ data = { elements; var count = 1 } }); - var size = 1 - } - }; - - /// Remove all the elements from the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let cities = Set.empty(); - /// Set.add(cities, Text.compare, "Zurich"); - /// Set.add(cities, Text.compare, "San Francisco"); - /// Set.add(cities, Text.compare, "London"); - /// assert Set.size(cities) == 3; - /// - /// Set.clear(cities); - /// assert Set.size(cities) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func clear(self : Set) { - let emptySet = empty(); - self.root := emptySet.root; - self.size := 0 - }; - - /// Determines whether a set is empty. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 3); - /// - /// assert not Set.isEmpty(set); - /// Set.clear(set); - /// assert Set.isEmpty(set); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func isEmpty(self : Set) : Bool { - self.size == 0 - }; - - /// Return the number of elements in a set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 3); - /// - /// assert Set.size(set) == 3; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func size(self : Set) : Nat { - self.size - }; - - /// Test whether two imperative sets are equal. - /// Both sets have to be constructed by the same comparison function. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([2, 1].values(), Nat.compare); - /// let set3 = Set.fromIter([2, 1, 0].values(), Nat.compare); - /// assert Set.equal(set1, set2, Nat.compare); - /// assert not Set.equal(set1, set3, Nat.compare); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func equal(self : Set, other : Set, compare : (implicit : (T, T) -> Types.Order)) : Bool { - if (self.size != other.size) return false; - // TODO: optimize - let iterator1 = values(self); - let iterator2 = values(other); - loop { - let next1 = iterator1.next(); - let next2 = iterator2.next(); - switch (next1, next2) { - case (null, null) { - return true - }; - case (?element1, ?element2) { - if (not (compare(element1, element2) == #equal)) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Tests whether the set contains the provided element. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 3); - /// - /// assert Set.contains(set, Nat.compare, 1); - /// assert not Set.contains(set, Nat.compare, 4); - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func contains(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Bool { - switch (self.root) { - case (#internal(internalNode)) { - containsInInternal(internalNode, compare, element) - }; - case (#leaf(leafNode)) { containsInLeaf(leafNode, compare, element) } - } - }; - - /// Add a new element to a set. - /// No effect if the element already exists in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// assert Iter.toArray(Set.values(set)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func add(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) { - ignore insert(self, compare, element) - }; - - /// Insert a new element in the set. - /// Returns true if the element is new, false if the element was already contained in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Set.insert(set, Nat.compare, 2); - /// assert Set.insert(set, Nat.compare, 1); - /// assert not Set.insert(set, Nat.compare, 2); - /// assert Iter.toArray(Set.values(set)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func insert(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Bool { - let insertResult = switch (self.root) { - case (#leaf(leafNode)) { - leafInsertHelper(leafNode, btreeOrder, compare, element) - }; - case (#internal(internalNode)) { - internalInsertHelper(internalNode, btreeOrder, compare, element) - } - }; - - switch (insertResult) { - case (#inserted) { - // if inserted an element that was not previously there, increment the tree size counter - self.size += 1; - true - }; - case (#existent) { - // keep size - false - }; - case (#promote({ element = promotedElement; leftChild; rightChild })) { - let elements = VarArray.repeat(null, btreeOrder - 1); - elements[0] := ?promotedElement; - let children = VarArray.repeat>(null, btreeOrder); - children[0] := ?leftChild; - children[1] := ?rightChild; - self.root := #internal({ - data = { elements; var count = 1 }; - children - }); - // promotion always comes from inserting a new element, so increment the tree size counter - self.size += 1; - true - } - } - }; - - /// Deletes an element from a set. - /// No effect if the element is not contained in the set. - /// - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// - /// Set.remove(set, Nat.compare, 2); - /// assert not Set.contains(set, Nat.compare, 2); - /// - /// Set.remove(set, Nat.compare, 4); - /// assert not Set.contains(set, Nat.compare, 4); - /// - /// assert Iter.toArray(Set.values(set)) == [1, 3]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - public func remove(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : () { - ignore delete(self, compare, element) - }; - - /// Deletes an element from a set. - /// Returns true if the element was contained in the set, false if not. - /// - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// - /// assert Set.delete(set, Nat.compare, 2); - /// assert not Set.contains(set, Nat.compare, 2); - /// - /// assert not Set.delete(set, Nat.compare, 4); - /// assert not Set.contains(set, Nat.compare, 4); - /// assert Iter.toArray(Set.values(set)) == [1, 3]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// @deprecated M0235 - public func delete(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Bool { - let deleted = switch (self.root) { - case (#leaf(leafNode)) { - // TODO: think about how this can be optimized so don't have to do two steps (search and then insert)? - switch (NodeUtil.getElementIndex(leafNode.data, compare, element)) { - case (#elementFound(deleteIndex)) { - leafNode.data.count -= 1; - ignore BTreeHelper.deleteAndShift(leafNode.data.elements, deleteIndex); - self.size -= 1; - true - }; - case _ { false } - } - }; - case (#internal(internalNode)) { - let deletedElement = switch (internalDeleteHelper(internalNode, btreeOrder, compare, element, false)) { - case (#deleted) { true }; - case (#inexistent) { false }; - case (#mergeChild({ internalChild })) { - if (internalChild.data.count > 0) { - self.root := #internal(internalChild) - } - // This case will be hit if the BTree has order == 4 - // In this case, the internalChild has no element (last element was merged with new child), so need to promote that merged child (its only child) - else { - self.root := switch (internalChild.children[0]) { - case (?node) { node }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.delete(), element deletion failed, due to a null replacement node error") - } - } - }; - true - } - }; - if (deletedElement) { - // if deleted an element from the BTree, decrement the size - self.size -= 1 - }; - deletedElement - } - }; - deleted - }; - - /// Retrieves the maximum element from the set. - /// If the set is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Set.max(set) == null; - /// Set.add(set, Nat.compare, 3); - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// assert Set.max(set) == ?3; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set. - public func max(self : Set) : ?T { - reverseValues(self).next() - }; - - /// Retrieves the minimum element from the set. - /// If the set is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Set.min(set) == null; - /// Set.add(set, Nat.compare, 1); - /// Set.add(set, Nat.compare, 2); - /// Set.add(set, Nat.compare, 3); - /// assert Set.min(set) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set. - public func min(self : Set) : ?T { - values(self).next() - }; - - public func toArray(self : Set) : [T] { - Iter.toArray(values(self)) - }; - - /// Returns an iterator over the elements in the set, - /// traversing the elements in the ascending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 3, 1].values(), Nat.compare); - /// - /// var tmp = ""; - /// for (number in Set.values(set)) { - /// tmp #= " " # Nat.toText(number); - /// }; - /// assert tmp == " 0 1 2 3"; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func values(self : Set) : Types.Iter { - switch (self.root) { - case (#leaf(leafNode)) { return leafElements(leafNode) }; - case (#internal(internalNode)) { internalElements(internalNode) } - } - }; - - /// Returns an iterator over the elements in the set, - /// starting from a given element in ascending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1].values(), Nat.compare); - /// assert Iter.toArray(Set.valuesFrom(set, Nat.compare, 1)) == [1, 3]; - /// assert Iter.toArray(Set.valuesFrom(set, Nat.compare, 2)) == [3]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func valuesFrom( - self : Set, - compare : (implicit : (T, T) -> Order.Order), - element : T - ) : Types.Iter { - switch (self.root) { - case (#leaf(leafNode)) leafElementsFrom(leafNode, compare, element); - case (#internal(internalNode)) internalElementsFrom(internalNode, compare, element) - } - }; - - /// Returns an iterator over the elements in the set, - /// traversing the elements in the descending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 3, 1].values(), Nat.compare); - /// - /// var tmp = ""; - /// for (number in Set.reverseValues(set)) { - /// tmp #= " " # Nat.toText(number); - /// }; - /// assert tmp == " 3 2 1 0"; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseValues(self : Set) : Types.Iter { - switch (self.root) { - case (#leaf(leafNode)) { return reverseLeafElements(leafNode) }; - case (#internal(internalNode)) { reverseInternalElements(internalNode) } - } - }; - - /// Returns an iterator over the elements in the set, - /// starting from a given element in descending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 1, 3].values(), Nat.compare); - /// assert Iter.toArray(Set.reverseValuesFrom(set, Nat.compare, 0)) == [0]; - /// assert Iter.toArray(Set.reverseValuesFrom(set, Nat.compare, 2)) == [1, 0]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseValuesFrom( - self : Set, - compare : (implicit : (T, T) -> Order.Order), - element : T - ) : Types.Iter { - switch (self.root) { - case (#leaf(leafNode)) reverseLeafElementsFrom(leafNode, compare, element); - case (#internal(internalNode)) reverseInternalElementsFrom(internalNode, compare, element) - } - }; - - /// Create a mutable set with the elements obtained from an iterator. - /// Potential duplicate elements in the iterator are ignored, i.e. - /// multiple occurrence of an equal element only occur once in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([3, 1, 2, 1].values(), Nat.compare); - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func fromIter(iter : Types.Iter, compare : (implicit : (T, T) -> Order.Order)) : Set { - let set = empty(); - for (element in iter) { - add(set, compare, element) - }; - set - }; - - /// Convert an iterator of elements to a mutable set. - /// Potential duplicate elements in the iterator are ignored, i.e. - /// multiple occurrence of an equal element only occur once in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = [3, 1, 2, 1].values(); - /// - /// let set = iter.toSet(Nat.compare); - /// - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func toSet(self : Types.Iter, compare : (implicit : (T, T) -> Order.Order)) : Set { - fromIter(self, compare) - }; - - /// Test whether `set1` is a sub-set of `set2`, i.e. each element in `set1` is - /// also contained in `set2`. Returns `true` if both sets are equal. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([2, 1, 0].values(), Nat.compare); - /// let set3 = Set.fromIter([3, 4].values(), Nat.compare); - /// assert Set.isSubset(set1, set2, Nat.compare); - /// assert not Set.isSubset(set1, set3, Nat.compare); - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func isSubset(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Bool { - if (self.size > other.size) { return false }; - // TODO: optimize - for (element in values(self)) { - if (not contains(other, compare, element)) { - return false - } - }; - true - }; - - /// Returns a new set that is the union of `set1` and `set2`, - /// i.e. a new set that all the elements that exist in at least on of the two sets. - /// Potential duplicates are ignored, i.e. if the same element occurs in both `set1` - /// and `set2`, it only occurs once in the returned set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let union = Set.union(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(union)) == [1, 2, 3, 4, 5]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func union(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = clone(self); - for (element in values(other)) { - if (not contains(result, compare, element)) { - add(result, compare, element) - } - }; - result - }; - - /// Returns a new set that is the intersection of `set1` and `set2`, - /// i.e. a new set that contains all the elements that exist in both sets. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let intersection = Set.intersection(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(intersection)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func intersection(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = empty(); - for (element in values(self)) { - if (contains(other, compare, element)) { - add(result, compare, element) - } - }; - result - }; - - /// Returns a new set that is the difference between `set1` and `set2` (`set1` minus `set2`), - /// i.e. a new set that contains all the elements of `set1` that do not exist in `set2`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let difference = Set.difference(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(difference)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func difference(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = empty(); - for (element in values(self)) { - if (not contains(other, compare, element)) { - add(result, compare, element) - } - }; - result - }; - - /// Adds all elements from `iter` to the specified `set`. - /// This is equivalent to `Set.union()` but modifies the set in place. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// Set.addAll(set, Nat.compare, [3, 4, 5].values()); - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3, 4, 5]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements in `set` and `iter`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func addAll(self : Set, compare : (implicit : (T, T) -> Order.Order), iter : Types.Iter) { - for (element in iter) { - add(self, compare, element) - } - }; - - /// Deletes all values in `iter` from the specified `set`. - /// Returns `true` if any value was present in the set, otherwise false. - /// The return value indicates whether the size of the set has changed. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 1, 2].values(), Nat.compare); - /// assert Set.deleteAll(set, Nat.compare, [0, 2].values()); - /// assert Iter.toArray(Set.values(set)) == [1]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements in `set` and `iter`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func deleteAll(self : Set, compare : (implicit : (T, T) -> Order.Order), iter : Types.Iter) : Bool { - var deleted = false; - for (element in iter) { - deleted := delete(self, compare, element) or deleted // order matters! - }; - deleted - }; - - /// Inserts all values in `iter` into `set`. - /// Returns true if any value was not contained in the original set, otherwise false. - /// The return value indicates whether the size of the set has changed. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 1, 2].values(), Nat.compare); - /// assert Set.insertAll(set, Nat.compare, [0, 2, 3].values()); - /// assert Iter.toArray(Set.values(set)) == [0, 1, 2, 3]; - /// assert not Set.insertAll(set, Nat.compare, [0, 1, 2].values()); // no change - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements in `set` and `iter`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// @deprecated M0235 - public func insertAll(self : Set, compare : (implicit : (T, T) -> Order.Order), iter : Types.Iter) : Bool { - var inserted = false; - for (element in iter) { - inserted := insert(self, compare, element) or inserted // order matters! - }; - inserted - }; - - /// Removes all values in `set` that do not satisfy the given predicate. - /// Returns `true` if and only if the size of the set has changed. - /// Modifies the set in place. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([3, 1, 2].values(), Nat.compare); - /// - /// let sizeChanged = Set.retainAll(set, Nat.compare, func n { n % 2 == 0 }); - /// assert Iter.toArray(Set.values(set)) == [2]; - /// assert sizeChanged; - /// } - /// ``` - public func retainAll(self : Set, compare : (implicit : (T, T) -> Order.Order), predicate : T -> Bool) : Bool { - let array = Array.fromIter(values(self)); - deleteAll( - self, - compare, - Iter.filter(array.vals(), func(element : T) : Bool = not predicate(element)) - ) - }; - - /// Apply an operation on each element contained in the set. - /// The operation is applied in ascending order of the elements. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// var tmp = ""; - /// Set.forEach(numbers, func (element) { - /// tmp #= " " # Nat.toText(element) - /// }); - /// assert tmp == " 0 1 2 3"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func forEach(self : Set, operation : T -> ()) { - for (element in values(self)) { - operation(element) - } - }; - - /// Filter elements in a new set. - /// Create a copy of the mutable set that only contains the elements - /// that fulfil the criterion function. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let evenNumbers = Set.filter(numbers, Nat.compare, func (number) { - /// number % 2 == 0 - /// }); - /// assert Iter.toArray(Set.values(evenNumbers)) == [0, 2]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func filter(self : Set, compare : (implicit : (T, T) -> Order.Order), criterion : T -> Bool) : Set { - let result = empty(); - for (element in values(self)) { - if (criterion(element)) { - add(result, compare, element) - } - }; - result - }; - - /// Project all elements of the set in a new set. - /// Apply a mapping function to each element in the set and - /// collect the mapped elements in a new mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([3, 1, 2].values(), Nat.compare); - /// - /// let textNumbers = - /// Set.map(numbers, Text.compare, Nat.toText); - /// assert Iter.toArray(Set.values(textNumbers)) == ["1", "2", "3"]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func map(self : Set, compare : (implicit : (T2, T2) -> Order.Order), project : T1 -> T2) : Set { - let result = empty(); - for (element1 in values(self)) { - let element2 = project(element1); - add(result, compare, element2) - }; - result - }; - - /// Filter all elements in the set by also applying a projection to the elements. - /// Apply a mapping function `project` to all elements in the set and collect all - /// elements, for which the function returns a non-null new element. Collect all - /// non-discarded new elements in a new mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([3, 0, 2, 1].values(), Nat.compare); - /// - /// let evenTextNumbers = Set.filterMap(numbers, Text.compare, func (number) { - /// if (number % 2 == 0) { - /// ?Nat.toText(number) - /// } else { - /// null // discard odd numbers - /// } - /// }); - /// assert Iter.toArray(Set.values(evenTextNumbers)) == ["0", "2"]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func filterMap(self : Set, compare : (implicit : (T2, T2) -> Order.Order), project : T1 -> ?T2) : Set { - let result = empty(); - for (element1 in values(self)) { - switch (project(element1)) { - case null {}; - case (?element2) add(result, compare, element2) - } - }; - result - }; - - /// Iterate all elements in ascending order, - /// and accumulate the elements by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1].values(), Nat.compare); - /// - /// let text = Set.foldLeft( - /// set, - /// "", - /// func (accumulator, element) { - /// accumulator # " " # Nat.toText(element) - /// } - /// ); - /// assert text == " 0 1 2 3"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func foldLeft( - self : Set, - base : A, - combine : (A, T) -> A - ) : A { - var accumulator = base; - for (element in values(self)) { - accumulator := combine(accumulator, element) - }; - accumulator - }; - - /// Iterate all elements in descending order, - /// and accumulate the elements by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1].values(), Nat.compare); - /// - /// let text = Set.foldRight( - /// set, - /// "", - /// func (element, accumulator) { - /// accumulator # " " # Nat.toText(element) - /// } - /// ); - /// assert text == " 3 2 1 0"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func foldRight( - self : Set, - base : A, - combine : (T, A) -> A - ) : A { - var accumulator = base; - for (element in reverseValues(self)) { - accumulator := combine(element, accumulator) - }; - accumulator - }; - - /// Construct the union of a series of sets, i.e. all elements of - /// each set are included in the result set. - /// Any duplicates are ignored, i.e. if an element occurs - /// in several of the iterated sets, it only occurs once in the result set. - /// - /// Assumes all sets are ordered by `compare`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let set3 = Set.fromIter([5, 6, 7].values(), Nat.compare); - /// let combined = Set.join([set1, set2, set3].values(), Nat.compare); - /// assert Iter.toArray(Set.values(combined)) == [1, 2, 3, 4, 5, 6, 7]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the iterated sets, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func join(setIterator : Types.Iter>, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = empty(); - for (set in setIterator) { - for (element in values(set)) { - add(result, compare, element) - } - }; - result - }; - - /// Construct the union of a set of element sets, i.e. all elements of - /// each element set are included in the result set. - /// Any duplicates are ignored, i.e. if the same element occurs in multiple element sets, - /// it only occurs once in the result set. - /// - /// Assumes all sets are ordered by `compare`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// import Order "mo:core/Order"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// func setCompare(first: Set.Set, second: Set.Set) : Order.Order { - /// Set.compare(first, second, Nat.compare) - /// }; - /// - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let set3 = Set.fromIter([5, 6, 7].values(), Nat.compare); - /// let setOfSets = Set.fromIter([set1, set2, set3].values(), setCompare); - /// let flatSet = Set.flatten(setOfSets, Nat.compare); - /// assert Iter.toArray(Set.values(flatSet)) == [1, 2, 3, 4, 5, 6, 7]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in all the sub-sets, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func flatten(self : Set>, compare : (implicit : (T, T) -> Order.Order)) : Set { - let result = empty(); - for (subSet in values(self)) { - for (element in values(subSet)) { - add(result, compare, element) - } - }; - result - }; - - /// Check whether all elements in the set satisfy a predicate, i.e. - /// the `predicate` function returns `true` for all elements in the set. - /// Returns `true` for an empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let belowTen = Set.all(set, func (number) { - /// number < 10 - /// }); - /// assert belowTen; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func all(self : Set, predicate : T -> Bool) : Bool { - // TODO optimize, avoiding iterator - for (element in values(self)) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Check whether at least one element in the set satisfies a predicate, i.e. - /// the `predicate` function returns `true` for at least one element in the set. - /// Returns `false` for an empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let aboveTen = Set.any(set, func (number) { - /// number > 10 - /// }); - /// assert not aboveTen; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func any(self : Set, predicate : T -> Bool) : Bool { - // TODO optimize, avoiding iterator - for (element in values(self)) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Internal sanity check function. - /// Can be used to check that elements have been inserted with a consistent comparison function. - /// Traps if the internal set structure is invalid. - /// @deprecated M0235 - public func assertValid(self : Set, compare : (implicit : (T, T) -> Order.Order)) { - func checkIteration(iterator : Types.Iter, order : Order.Order) { - switch (iterator.next()) { - case null {}; - case (?first) { - var previous = first; - loop { - switch (iterator.next()) { - case null return; - case (?next) { - if (compare(previous, next) != order) { - Runtime.trap("Invalid order") - }; - previous := next - } - } - } - } - } - }; - checkIteration(values(self), #less); - checkIteration(reverseValues(self), #greater) - }; - - /// Generate a textual representation of all the elements in the set. - /// Primarily to be used for testing and debugging. - /// The elements are formatted according to `elementFormat`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// assert Set.toText(set, Nat.toText) == "Set{0, 1, 2, 3}" - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that `elementFormat` has runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func toText(self : Set, toText : (implicit : T -> Text)) : Text { - var text = "Set{"; - var sep = ""; - for (element in values(self)) { - text #= sep # toText(element); - sep := ", " - }; - text # "}" - }; - - /// Compare two sets by comparing the elements. - /// Both sets must have been created by the same comparison function. - /// The two sets are iterated by the ascending order of their creation and - /// order is determined by the following rules: - /// Less: - /// `set1` is less than `set2` if: - /// * the pairwise iteration hits an element pair `element1` and `element2` where - /// `element1` is less than `element2` and all preceding elements are equal, or, - /// * `set1` is a strict prefix of `set2`, i.e. `set2` has more elements than `set1` - /// and all elements of `set1` occur at the beginning of iteration `set2`. - /// Equal: - /// `set1` and `set2` have same series of equal elements by pairwise iteration. - /// Greater: - /// `set1` is neither less nor equal `set2`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 1].values(), Nat.compare); - /// let set2 = Set.fromIter([0, 2].values(), Nat.compare); - /// - /// assert Set.compare(set1, set2, Nat.compare) == #less; - /// assert Set.compare(set1, set1, Nat.compare) == #equal; - /// assert Set.compare(set2, set1, Nat.compare) == #greater; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that `compare` has runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func compare(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let iterator1 = values(self); - let iterator2 = values(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?element1, ?element2) { - let comparison = compare(element1, element2); - if (comparison != #equal) { - return comparison - } - } - } - } - }; - - func leafElements({ data } : Leaf) : Types.Iter { - var i : Nat = 0; - object { - public func next() : ?T { - if (i >= data.count) { - null - } else { - let res = data.elements[i]; - i += 1; - res - } - } - } - }; - - func leafElementsFrom({ data } : Leaf, compare : (T, T) -> Order.Order, element : T) : Types.Iter { - var i = switch (BinarySearch.binarySearchNode(data.elements, compare, element, data.count)) { - case (#elementFound(i)) i; - case (#notFound(i)) i - }; - object { - public func next() : ?T { - if (i >= data.count) { - null - } else { - let res = data.elements[i]; - i += 1; - res - } - } - } - }; - - func reverseLeafElements({ data } : Leaf) : Types.Iter { - var i : Nat = data.count; - object { - public func next() : ?T { - if (i == 0) { - null - } else { - let res = data.elements[i - 1]; - i -= 1; - res - } - } - } - }; - - func reverseLeafElementsFrom({ data } : Leaf, compare : (T, T) -> Order.Order, element : T) : Types.Iter { - var i = switch (BinarySearch.binarySearchNode(data.elements, compare, element, data.count)) { - case (#elementFound(i)) i + 1; // +1 to include this element - case (#notFound(i)) i // i is the index of the first element greater than the search element, or count if all elements are less than the search element - }; - object { - public func next() : ?T { - if (i == 0) { - null - } else { - let res = data.elements[i - 1]; - i -= 1; - res - } - } - } - }; - - // Cursor type that keeps track of the current node and the current element index in the node - type NodeCursor = { node : Node; elementIndex : Nat }; - - func internalElements(internal : Internal) : Types.Iter { - // The nodeCursorStack keeps track of the current node and the current element index in the node - // We use a stack here to push to/pop off the next node cursor to visit - let nodeCursorStack = initializeForwardNodeCursorStack(internal); - internalElementsFromStack(nodeCursorStack) - }; - - func internalElementsFrom(internal : Internal, compare : (T, T) -> Order.Order, element : T) : Types.Iter { - let nodeCursorStack = initializeForwardNodeCursorStackFrom(internal, compare, element); - internalElementsFromStack(nodeCursorStack) - }; - - func internalElementsFromStack(nodeCursorStack : Stack.Stack>) : Types.Iter { - object { - public func next() : ?T { - // pop the next node cursor off the stack - var nodeCursor = Stack.pop(nodeCursorStack); - switch (nodeCursor) { - case null { return null }; - case (?{ node; elementIndex }) { - switch (node) { - // if a leaf node, iterate through the leaf node's next element - case (#leaf(leafNode)) { - let lastIndex = leafNode.data.count - 1 : Nat; - if (elementIndex > lastIndex) { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalElements(), leaf elementIndex out of bounds") - }; - - let currentElement = switch (leafNode.data.elements[elementIndex]) { - case (?element) { element }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Set.internalElements(), null element found in leaf node." - # "leafNode.data.count=" # debug_show (leafNode.data.count) # ", elementIndex=" # debug_show (elementIndex) - ) - } - }; - // if not at the last element, push the next element index of the leaf onto the stack and return the current element - if (elementIndex < lastIndex) { - Stack.push( - nodeCursorStack, - { - node = #leaf(leafNode); - elementIndex = elementIndex + 1 : Nat - } - ) - }; - - ?currentElement - }; - // if an internal node - case (#internal(internalNode)) { - let lastIndex = internalNode.data.count - 1 : Nat; - // Developer facing message in case of a bug - if (elementIndex > lastIndex) { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalElements(), internal elementIndex out of bounds") - }; - - let currentElement = switch (internalNode.data.elements[elementIndex]) { - case (?element) { element }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Set.internalElements(), null element found in internal node. " # - "internal.data.count=" # debug_show (internalNode.data.count) # ", elementIndex=" # debug_show (elementIndex) - ) - } - }; - - let nextCursor = { - node = #internal(internalNode); - elementIndex = elementIndex + 1 : Nat - }; - // if not the last element, push the next element of the internal node onto the stack - if (elementIndex < lastIndex) { - Stack.push(nodeCursorStack, nextCursor) - }; - // traverse the next child's min subtree and push the resulting node cursors onto the stack - // then return the current element of the internal node - traverseMinSubtreeIter(nodeCursorStack, nextCursor); - ?currentElement - } - } - } - } - } - } - }; - - func reverseInternalElements(internal : Internal) : Types.Iter { - // The nodeCursorStack keeps track of the current node and the current element index in the node - // We use a stack here to push to/pop off the next node cursor to visit - let nodeCursorStack = initializeReverseNodeCursorStack(internal); - reverseInternalElementsFromStack(nodeCursorStack) - }; - - func reverseInternalElementsFrom(internal : Internal, compare : (T, T) -> Order.Order, element : T) : Types.Iter { - let nodeCursorStack = initializeReverseNodeCursorStackFrom(internal, compare, element); - reverseInternalElementsFromStack(nodeCursorStack) - }; - - func reverseInternalElementsFromStack(nodeCursorStack : Stack.Stack>) : Types.Iter { - object { - public func next() : ?T { - // pop the next node cursor off the stack - var nodeCursor = Stack.pop(nodeCursorStack); - switch (nodeCursor) { - case null { return null }; - case (?{ node; elementIndex }) { - let firstIndex = 0 : Nat; - assert (elementIndex > firstIndex); - switch (node) { - // if a leaf node, reverse iterate through the leaf node's next element - case (#leaf(leafNode)) { - let currentElement = switch (leafNode.data.elements[elementIndex - 1]) { - case (?element) { element }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Set.reverseInternalElements(), null element found in leaf node." - # "leafNode.data.count=" # debug_show (leafNode.data.count) # ", elementIndex=" # debug_show (elementIndex) - ) - } - }; - // if not at the last element, push the previous element index of the leaf onto the stack and return the current element - if (elementIndex - 1 : Nat > firstIndex) { - Stack.push( - nodeCursorStack, - { - node = #leaf(leafNode); - elementIndex = elementIndex - 1 : Nat - } - ) - }; - - // return the current element - ?currentElement - }; - // if an internal node - case (#internal(internalNode)) { - let currentElement = switch (internalNode.data.elements[elementIndex - 1]) { - case (?element) { element }; - case null { - Runtime.trap( - "UNREACHABLE_ERROR: file a bug report! In Set.reverseInternalElements(), null element found in internal node. " # - "internal.data.count=" # debug_show (internalNode.data.count) # ", elementIndex=" # debug_show (elementIndex) - ) - } - }; - - let previousCursor = { - node = #internal(internalNode); - elementIndex = elementIndex - 1 : Nat - }; - // if not the first element, push the previous element index of the internal node onto the stack - if (elementIndex - 1 : Nat > firstIndex) { - Stack.push(nodeCursorStack, previousCursor) - }; - // traverse the previous child's max subtree and push the resulting node cursors onto the stack - // then return the current element of the internal node - traverseMaxSubtreeIter(nodeCursorStack, previousCursor); - ?currentElement - } - } - } - } - } - } - }; - - func initializeForwardNodeCursorStack(internal : Internal) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - elementIndex = 0 - }; - - // push the initial cursor to the stack - Stack.push(nodeCursorStack, nodeCursor); - // then traverse left - traverseMinSubtreeIter(nodeCursorStack, nodeCursor); - nodeCursorStack - }; - - func initializeForwardNodeCursorStackFrom(internal : Internal, compare : (T, T) -> Order.Order, element : T) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - elementIndex = 0 - }; - - traverseMinSubtreeIterFrom(nodeCursorStack, nodeCursor, compare, element); - nodeCursorStack - }; - - func initializeReverseNodeCursorStack(internal : Internal) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - elementIndex = internal.data.count - }; - - // push the initial cursor to the stack - Stack.push(nodeCursorStack, nodeCursor); - // then traverse left - traverseMaxSubtreeIter(nodeCursorStack, nodeCursor); - nodeCursorStack - }; - - func initializeReverseNodeCursorStackFrom(internal : Internal, compare : (T, T) -> Order.Order, element : T) : Stack.Stack> { - let nodeCursorStack = Stack.empty>(); - let nodeCursor : NodeCursor = { - node = #internal(internal); - elementIndex = internal.data.count - }; - - traverseMaxSubtreeIterFrom(nodeCursorStack, nodeCursor, compare, element); - nodeCursorStack - }; - - // traverse the min subtree of the current node cursor, passing each new element to the node cursor stack - func traverseMinSubtreeIter(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor) { - var currentNode = nodeCursor.node; - var childIndex = nodeCursor.elementIndex; - - label l loop { - switch (currentNode) { - // If currentNode is leaf, have hit the minimum element of the subtree and already pushed it's cursor to the stack - // so can return - case (#leaf(_)) { - return - }; - // If currentNode is internal, add it's left most child to the stack and continue traversing - case (#internal(internalNode)) { - switch (internalNode.children[childIndex]) { - // Push the next min (left most) child node to the stack - case (?childNode) { - childIndex := 0; - currentNode := childNode; - Stack.push( - nodeCursorStack, - { - node = currentNode; - elementIndex = childIndex - } - ) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.traverseMinSubtreeIter(), null child node error") - } - } - } - } - } - }; - - func traverseMinSubtreeIterFrom(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor, compare : (T, T) -> Order.Order, element : T) { - var currentNode = nodeCursor.node; - - label l loop { - let (node, childrenOption) = switch (currentNode) { - case (#leaf(leafNode)) (leafNode, null); - case (#internal(internalNode)) (internalNode, ?internalNode.children) - }; - let (i, isFound) = switch (NodeUtil.getElementIndex(node.data, compare, element)) { - case (#elementFound(i)) (i, true); - case (#notFound(i)) (i, false) - }; - if (i < node.data.count) { - Stack.push( - nodeCursorStack, - { - node = currentNode; - elementIndex = i // greater elements to traverse - } - ) - }; - if isFound return; - let ?children = childrenOption else return; - let ?childNode = children[i] else Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.traverseMinSubtreeIterFrom(), null child node error"); - currentNode := childNode - } - }; - - // traverse the max subtree of the current node cursor, passing each new element to the node cursor stack - func traverseMaxSubtreeIter(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor) { - var currentNode = nodeCursor.node; - var childIndex = nodeCursor.elementIndex; - - label l loop { - switch (currentNode) { - // If currentNode is leaf, have hit the maximum element of the subtree and already pushed it's cursor to the stack - // so can return - case (#leaf(_)) { - return - }; - // If currentNode is internal, add it's right most child to the stack and continue traversing - case (#internal(internalNode)) { - assert (childIndex <= internalNode.data.count); // children are one more than data elements - switch (internalNode.children[childIndex]) { - // Push the next max (right most) child node to the stack - case (?childNode) { - childIndex := switch (childNode) { - case (#internal(internalNode)) internalNode.data.count; - case (#leaf(leafNode)) leafNode.data.count - }; - currentNode := childNode; - Stack.push( - nodeCursorStack, - { - node = currentNode; - elementIndex = childIndex - } - ) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.traverseMaxSubtreeIter(), null child node error") - } - } - } - } - } - }; - - func traverseMaxSubtreeIterFrom(nodeCursorStack : Stack.Stack>, nodeCursor : NodeCursor, compare : (T, T) -> Order.Order, element : T) { - var currentNode = nodeCursor.node; - - label l loop { - let (node, childrenOption) = switch (currentNode) { - case (#leaf(leafNode)) (leafNode, null); - case (#internal(internalNode)) (internalNode, ?internalNode.children) - }; - let (i, isFound) = switch (NodeUtil.getElementIndex(node.data, compare, element)) { - case (#elementFound(i)) (i + 1, true); // +1 to include this element - case (#notFound(i)) (i, false) // i is the index of the first element less than the search element, or 0 if all elements are greater than the search element - }; - if (i > 0) { - Stack.push( - nodeCursorStack, - { - node = currentNode; - elementIndex = i - } - ) - }; - if isFound return; - let ?children = childrenOption else return; - let ?childNode = children[i] else Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.traverseMaxSubtreeIterFrom(), null child node error"); - currentNode := childNode - } - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateInternalDeleteResult = { - // element was deleted - #deleted; - // element was absent - #inexistent; - // deleted an element, but was unable to successfully borrow and rebalance at the previous level without merging children - // the internalChild is the merged child that needs to be rebalanced at the next level up in the BTree - #mergeChild : { - internalChild : Internal - } - }; - - func internalDeleteHelper(internalNode : Internal, order : Nat, compare : (T, T) -> Order.Order, deleteElement : T, skipNode : Bool) : IntermediateInternalDeleteResult { - let minElements = NodeUtil.minElementsFromOrder(order); - let elementIndex = NodeUtil.getElementIndex(internalNode.data, compare, deleteElement); - - // match on both the result of the node binary search, and if this node level should be skipped even if the element is found (internal element replacement case) - switch (elementIndex, skipNode) { - // if element is found in the internal node - case (#elementFound(deleteIndex), false) { - if (Option.isNull(internalNode.data.elements[deleteIndex])) { - Runtime.trap("Bug in Set.internalDeleteHelper") - }; - // TODO: (optimization) replace with deletion in one step without having to retrieve the max element first - let replaceElement = NodeUtil.getMaxElement(internalNode.children[deleteIndex]); - internalNode.data.elements[deleteIndex] := ?replaceElement; - switch (internalDeleteHelper(internalNode, order, compare, replaceElement, true)) { - case (#deleted) { #deleted }; - case (#inexistent) { #inexistent }; - case (#mergeChild({ internalChild })) { - #mergeChild({ internalChild }) - } - } - }; - // if element is not found in the internal node OR the element is found, but skipping this node (because deleting the in order precessor i.e. replacement element) - // in both cases need to descend and traverse to find the element to delete - case ((#elementFound(_), true) or (#notFound(_), _)) { - let childIndex = switch (elementIndex) { - case (#elementFound(replacedSkipElementIndex)) { - replacedSkipElementIndex - }; - case (#notFound(childIndex)) { childIndex } - }; - let child = switch (internalNode.children[childIndex]) { - case (?c) { c }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalDeleteHelper, child index of #elementFound or #notfound is null") - } - }; - switch (child) { - // if child is internal - case (#internal(internalChild)) { - switch (internalDeleteHelper(internalChild, order, compare, deleteElement, false), childIndex == 0) { - // if element was successfully deleted and no additional tree re-balancing is needed, return #deleted - case (#deleted, _) { #deleted }; - case (#inexistent, _) { #inexistent }; - // if internalChild needs rebalancing and pulling child is left most - case (#mergeChild({ internalChild }), true) { - // try to pull left-most element and child from right sibling - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex + 1, #successor)) { - // if can pull up sibling element and child - case (#borrowed({ deletedSiblingElement; child })) { - NodeUtil.rotateBorrowedElementsAndChildFromSibling( - internalNode, - childIndex, - deletedSiblingElement, - child, - internalChild, - #right - ); - #deleted - }; - // unable to pull from sibling, need to merge with right sibling and push down parent - case (#notEnoughElements(sibling)) { - // get the parent element that will be pushed down the the child - let elementsToBePushedToChild = ?BTreeHelper.deleteAndShift(internalNode.data.elements, 0); - internalNode.data.count -= 1; - // merge the children and push down the parent - let newChild = NodeUtil.mergeChildrenAndPushDownParent(internalChild, elementsToBePushedToChild, sibling); - // update children of the parent - internalNode.children[0] := ?#internal(newChild); - ignore ?BTreeHelper.deleteAndShift(internalNode.children, 1); - - if (internalNode.data.count < minElements) { - #mergeChild({ internalChild = internalNode }) - } else { - #deleted - } - } - } - }; - // if internalChild needs rebalancing and pulling child is > 0, so a left sibling exists - case (#mergeChild({ internalChild }), false) { - // try to pull right-most element and its child directly from left sibling - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex - 1 : Nat, #predecessor)) { - case (#borrowed({ deletedSiblingElement; child })) { - NodeUtil.rotateBorrowedElementsAndChildFromSibling( - internalNode, - childIndex - 1 : Nat, - deletedSiblingElement, - child, - internalChild, - #left - ); - #deleted - }; - // unable to pull from left sibling - case (#notEnoughElements(leftSibling)) { - // if child is not last index, try to pull from the right child - if (childIndex < internalNode.data.count) { - switch (NodeUtil.borrowFromInternalSibling(internalNode.children, childIndex, #successor)) { - // if can pull up sibling element and child - case (#borrowed({ deletedSiblingElement; child })) { - NodeUtil.rotateBorrowedElementsAndChildFromSibling( - internalNode, - childIndex, - deletedSiblingElement, - child, - internalChild, - #right - ); - return #deleted - }; - // if cannot borrow, from left or right, merge (see below) - case _ {} - } - }; - - // get the parent element that will be pushed down the the child - let elementToBePushedToChild = ?BTreeHelper.deleteAndShift(internalNode.data.elements, childIndex - 1 : Nat); - internalNode.data.count -= 1; - // merge it the children and push down the parent - let newChild = NodeUtil.mergeChildrenAndPushDownParent(leftSibling, elementToBePushedToChild, internalChild); - - // update children of the parent - internalNode.children[childIndex - 1] := ?#internal(newChild); - ignore ?BTreeHelper.deleteAndShift(internalNode.children, childIndex); - - if (internalNode.data.count < minElements) { - #mergeChild({ internalChild = internalNode }) - } else { - #deleted - } - } - } - } - } - }; - // if child is leaf - case (#leaf(leafChild)) { - switch (leafDeleteHelper(leafChild, order, compare, deleteElement), childIndex == 0) { - case (#deleted, _) { #deleted }; - case (#inexistent, _) { #inexistent }; - // if delete child is left most, try to borrow from right child - case (#mergeLeafData({ leafDeleteIndex }), true) { - switch (NodeUtil.borrowFromRightLeafChild(internalNode.children, childIndex)) { - case (?borrowedElement) { - let elementToBePushedToChild = internalNode.data.elements[childIndex]; - internalNode.data.elements[childIndex] := ?borrowedElement; - - ignore BTreeHelper.insertAtPostionAndDeleteAtPosition(leafChild.data.elements, elementToBePushedToChild, leafChild.data.count - 1, leafDeleteIndex); - #deleted - }; - - case null { - // can't borrow from right child, delete from leaf and merge with right child and parent element, then push down into new leaf - let rightChild = switch (internalNode.children[childIndex + 1]) { - case (?#leaf(rc)) { rc }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalDeleteHelper, if trying to borrow from right leaf child is null, rightChild index cannot be null or internal") - } - }; - let mergedLeaf = mergeParentWithLeftRightChildLeafNodesAndDelete( - internalNode.data.elements[childIndex], - leafChild, - rightChild, - leafDeleteIndex, - #left - ); - // delete the left most internal node element, since was merging from a deletion in left most child (0) and the parent element was pushed into the mergedLeaf - ignore BTreeHelper.deleteAndShift(internalNode.data.elements, 0); - // update internal node children - BTreeHelper.replaceTwoWithElementAndShift>(internalNode.children, #leaf(mergedLeaf), 0); - internalNode.data.count -= 1; - - if (internalNode.data.count < minElements) { - #mergeChild({ - internalChild = internalNode - }) - } else { - #deleted - } - - } - } - }; - // if delete child is middle or right most, try to borrow from left child - case (#mergeLeafData({ leafDeleteIndex }), false) { - // if delete child is right most, try to borrow from left child - switch (NodeUtil.borrowFromLeftLeafChild(internalNode.children, childIndex)) { - case (?borrowedElement) { - let elementToBePushedToChild = internalNode.data.elements[childIndex - 1]; - internalNode.data.elements[childIndex - 1] := ?borrowedElement; - ignore BTreeHelper.insertAtPostionAndDeleteAtPosition(leafChild.data.elements, elementToBePushedToChild, 0, leafDeleteIndex); - #deleted - }; - case null { - // if delete child is in the middle, try to borrow from right child - if (childIndex < internalNode.data.count) { - // try to borrow from right - switch (NodeUtil.borrowFromRightLeafChild(internalNode.children, childIndex)) { - case (?borrowedElement) { - let elementToBePushedToChild = internalNode.data.elements[childIndex]; - internalNode.data.elements[childIndex] := ?borrowedElement; - // insert the successor at the very last element - ignore BTreeHelper.insertAtPostionAndDeleteAtPosition(leafChild.data.elements, elementToBePushedToChild, leafChild.data.count - 1, leafDeleteIndex); - return #deleted - }; - // if cannot borrow, from left or right, merge (see below) - case _ {} - } - }; - - // can't borrow from left child, delete from leaf and merge with left child and parent element, then push down into new leaf - let leftChild = switch (internalNode.children[childIndex - 1]) { - case (?#leaf(lc)) { lc }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.internalDeleteHelper, if trying to borrow from left leaf child is null, then left child index must not be null or internal") - } - }; - let mergedLeaf = mergeParentWithLeftRightChildLeafNodesAndDelete( - internalNode.data.elements[childIndex - 1], - leftChild, - leafChild, - leafDeleteIndex, - #right - ); - // delete the right most internal node element, since was merging from a deletion in the right most child and the parent element was pushed into the mergedLeaf - ignore BTreeHelper.deleteAndShift(internalNode.data.elements, childIndex - 1); - // update internal node children - BTreeHelper.replaceTwoWithElementAndShift>(internalNode.children, #leaf(mergedLeaf), childIndex - 1); - internalNode.data.count -= 1; - - if (internalNode.data.count < minElements) { - #mergeChild({ - internalChild = internalNode - }) - } else { - #deleted - } - } - } - } - } - } - } - } - } - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateLeafDeleteResult = { - // element was deleted - #deleted; - // element was absent - #inexistent; - // leaf had the minimum number of elements when deleting, so returns the leaf node's data and the index of the element that will be deleted - #mergeLeafData : { - data : Data; - leafDeleteIndex : Nat - } - }; - - func leafDeleteHelper(leafNode : Leaf, order : Nat, compare : (T, T) -> Order.Order, deleteElement : T) : IntermediateLeafDeleteResult { - let minElements = NodeUtil.minElementsFromOrder(order); - - switch (NodeUtil.getElementIndex(leafNode.data, compare, deleteElement)) { - case (#elementFound(deleteIndex)) { - if (leafNode.data.count > minElements) { - leafNode.data.count -= 1; - ignore BTreeHelper.deleteAndShift(leafNode.data.elements, deleteIndex); - #deleted - } else { - #mergeLeafData({ - data = leafNode.data; - leafDeleteIndex = deleteIndex - }) - } - }; - case (#notFound(_)) { - #inexistent - } - } - }; - - func containsInInternal(internalNode : Internal, compare : (T, T) -> Order.Order, element : T) : Bool { - switch (NodeUtil.getElementIndex(internalNode.data, compare, element)) { - case (#elementFound _index) { - true - }; - case (#notFound(index)) { - switch (internalNode.children[index]) { - // expects the child to be there, otherwise there's a bug in binary search or the tree is invalid - case null { Runtime.trap("Internal bug: Set.containsInInternal") }; - case (?#leaf(leafNode)) { containsInLeaf(leafNode, compare, element) }; - case (?#internal(internalNode)) { - containsInInternal(internalNode, compare, element) - } - } - } - } - }; - - func containsInLeaf(leafNode : Leaf, compare : (T, T) -> Order.Order, element : T) : Bool { - switch (NodeUtil.getElementIndex(leafNode.data, compare, element)) { - case (#elementFound(_index)) { - true - }; - case _ false - } - }; - - type DeletionSide = { #left; #right }; - - func mergeParentWithLeftRightChildLeafNodesAndDelete( - parentElement : ?T, - leftChild : Leaf, - rightChild : Leaf, - deleteIndex : Nat, - deletionSide : DeletionSide - ) : Leaf { - let count = leftChild.data.count * 2; - let (elements, _) = BTreeHelper.mergeParentWithChildrenAndDelete( - parentElement, - leftChild.data.count, - leftChild.data.elements, - rightChild.data.elements, - deleteIndex, - deletionSide - ); - ({ - data = { - elements; - var count = count - } - }) - }; - - // This type is used to signal to the parent calling context what happened in the level below - type IntermediateInsertResult = { - // element was inserted - #inserted; - // element was alreay present - #existent; - // child was full when inserting, so returns the promoted element and the split left and right child - #promote : { - element : T; - leftChild : Node; - rightChild : Node - } - }; - - // Helper for inserting into a leaf node - func leafInsertHelper(leafNode : Leaf, order : Nat, compare : (T, T) -> Order.Order, insertedElement : T) : (IntermediateInsertResult) { - // Perform binary search to see if the element exists in the node - switch (NodeUtil.getElementIndex(leafNode.data, compare, insertedElement)) { - case (#elementFound(insertIndex)) { - let previous = leafNode.data.elements[insertIndex]; - leafNode.data.elements[insertIndex] := ?insertedElement; - switch (previous) { - case (?_) { #existent }; - case null { Runtime.trap("Bug in Set.leafInsertHelper") }; // the binary search already found an element, so this case should never happen - } - }; - case (#notFound(insertIndex)) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - let maxElements : Nat = order - 1; - // If the leaf is full, insert, split the node, and promote the middle element - if (leafNode.data.count >= maxElements) { - let (leftElements, promotedParentElement, rightElements) = BTreeHelper.insertOneAtIndexAndSplitArray( - leafNode.data.elements, - insertedElement, - insertIndex - ); - - let leftCount = order / 2; - let rightCount : Nat = if (order % 2 == 0) { leftCount - 1 } else { - leftCount - }; - - ( - #promote({ - element = promotedParentElement; - leftChild = createLeaf(leftElements, leftCount); - rightChild = createLeaf(rightElements, rightCount) - }) - ) - } - // Otherwise, insert at the specified index (shifting elements over if necessary) - else { - NodeUtil.insertAtIndexOfNonFullNodeData(leafNode.data, ?insertedElement, insertIndex); - #inserted - } - } - } - }; - - // Helper for inserting into an internal node - func internalInsertHelper(internalNode : Internal, order : Nat, compare : (T, T) -> Order.Order, insertElement : T) : IntermediateInsertResult { - switch (NodeUtil.getElementIndex(internalNode.data, compare, insertElement)) { - case (#elementFound(insertIndex)) { - let previous = internalNode.data.elements[insertIndex]; - internalNode.data.elements[insertIndex] := ?insertElement; - switch (previous) { - case (?_) { #existent }; - case null { - Runtime.trap("Bug in Set.internalInsertHelper, element found") - }; // the binary search already found an element, so this case should never happen - } - }; - case (#notFound(insertIndex)) { - let insertResult = switch (internalNode.children[insertIndex]) { - case null { - Runtime.trap("Bug in Set.internalInsertHelper, not found") - }; - case (?#leaf(leafNode)) { - leafInsertHelper(leafNode, order, compare, insertElement) - }; - case (?#internal(internalChildNode)) { - internalInsertHelper(internalChildNode, order, compare, insertElement) - } - }; - - switch (insertResult) { - case (#inserted) #inserted; - case (#existent) #existent; - case (#promote({ element = promotedElement; leftChild; rightChild })) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - let maxElements : Nat = order - 1; - // if current internal node is full, need to split the internal node - if (internalNode.data.count >= maxElements) { - // insert and split internal elements, determine new promotion target element - let (leftElements, promotedParentElement, rightElements) = BTreeHelper.insertOneAtIndexAndSplitArray( - internalNode.data.elements, - promotedElement, - insertIndex - ); - - // calculate the element count in the left elements and the element count in the right elements - let leftCount = order / 2; - let rightCount : Nat = if (order % 2 == 0) { leftCount - 1 } else { - leftCount - }; - - // split internal children - let (leftChildren, rightChildren) = NodeUtil.splitChildrenInTwoWithRebalances( - internalNode.children, - insertIndex, - leftChild, - rightChild - ); - - // send the element to be promoted, as well as the internal children left and right split - #promote({ - element = promotedParentElement; - leftChild = #internal({ - data = { elements = leftElements; var count = leftCount }; - children = leftChildren - }); - rightChild = #internal({ - data = { elements = rightElements; var count = rightCount }; - children = rightChildren - }) - }) - } else { - // insert the new elements into the internal node - NodeUtil.insertAtIndexOfNonFullNodeData(internalNode.data, ?promotedElement, insertIndex); - // split and re-insert the single child that needs rebalancing - NodeUtil.insertRebalancedChild(internalNode.children, insertIndex, leftChild, rightChild); - #inserted - } - } - } - } - } - }; - - func createLeaf(elements : [var ?T], count : Nat) : Node { - #leaf({ - data = { - elements; - var count - } - }) - }; - - // FIXME - // Additional functionality compared to original source. - - func cloneData(data : Data) : Data { - { - elements = VarArray.clone(data.elements); - var count = data.count - } - }; - - func cloneNode(node : Node) : Node { - switch node { - case (#leaf { data }) { - #leaf { data = cloneData(data) } - }; - case (#internal { data; children }) { - let clonedData = cloneData(data); - let clonedChildren = VarArray.map, ?Node>( - children, - func child { - switch child { - case null null; - case (?childNode) ?cloneNode(childNode) - } - } - ); - #internal({ - data = clonedData; - children = clonedChildren - }) - } - } - }; - - module BinarySearch { - public type SearchResult = { - #elementFound : Nat; - #notFound : Nat - }; - - /// Searches an array for a specific element, returning the index it occurs at if #elementFound, or the child/insert index it may occur at - /// if #notFound. This is used when determining if a element exists in an internal or leaf node, where an element should be inserted in a - /// leaf node, or which child of an internal node a element could be in. - /// - /// Note: This function expects a mutable, nullable, array of elements in sorted order, where all nulls appear at the end of the array. - /// This function may trap if a null element appears before any elements. It also expects a maxIndex, which is the right-most index (bound) - /// from which to begin the binary search (the left most bound is expected to be 0) - /// - /// Parameters: - /// - /// * array - the sorted array that the binary search is performed upon - /// * compare - the comparator used to perform the search - /// * searchElement - the element being compared against in the search - /// * maxIndex - the right-most index (bound) from which to begin the search - public func binarySearchNode(array : [var ?T], compare : (T, T) -> Order.Order, searchElement : T, maxIndex : Nat) : SearchResult { - // TODO: get rid of this check? - // Trap if array is size 0 (should not happen) - if (array.size() == 0) { - assert false - }; - - // if all elements in the array are null (i.e. first element is null), return #notFound(0) - if (maxIndex == 0) { - return #notFound(0) - }; - - // Initialize search from first to last index - var left : Nat = 0; - var right = maxIndex; // maxIndex does not necessarily mean array.size() - 1 - // Search the array - while (left < right) { - let middle = (left + right) / 2; - switch (array[middle]) { - case null { assert false }; - case (?element) { - switch (compare(searchElement, element)) { - // If the element is present at the middle itself - case (#equal) { return #elementFound(middle) }; - // If element is greater than mid, it can only be present in left subarray - case (#greater) { left := middle + 1 }; - // If element is smaller than mid, it can only be present in right subarray - case (#less) { - right := if (middle == 0) { 0 } else { middle - 1 } - } - } - } - } - }; - - if (left == array.size()) { - return #notFound(left) - }; - - // left == right - switch (array[left]) { - // inserting at end of array - case null { #notFound(left) }; - case (?element) { - switch (compare(searchElement, element)) { - // if left is the searched element - case (#equal) { #elementFound(left) }; - // if the element is not found, return notFound and the insert location - case (#greater) { #notFound(left + 1) }; - case (#less) { #notFound(left) } - } - } - } - } - }; - - module NodeUtil { - /// Inserts element at the given index into a non-full leaf node - public func insertAtIndexOfNonFullNodeData(data : Data, element : ?T, insertIndex : Nat) { - let currentLastElementIndex : Nat = if (data.count == 0) { 0 } else { - data.count - 1 - }; - BTreeHelper.insertAtPosition(data.elements, element, insertIndex, currentLastElementIndex); - - // increment the count of data in this node since just inserted an element - data.count += 1 - }; - - /// Inserts two rebalanced (split) child halves into a non-full array of children. - public func insertRebalancedChild(children : [var ?Node], rebalancedChildIndex : Nat, leftChildInsert : Node, rightChildInsert : Node) { - // Note: BTree will always have an order >= 4, so this will never have negative Nat overflow - var j : Nat = children.size() - 2; - - // This is just a sanity check to ensure the children aren't already full (should split promote otherwise) - // TODO: Remove this check once confident - if (Option.isSome(children[j + 1])) { assert false }; - - // Iterate backwards over the array and shift each element over to the right by one until the rebalancedChildIndex is hit - while (j > rebalancedChildIndex) { - children[j + 1] := children[j]; - j -= 1 - }; - - // Insert both the left and right rebalanced children (replacing the pre-split child) - children[j] := ?leftChildInsert; - children[j + 1] := ?rightChildInsert - }; - - /// Used when splitting the children of an internal node - /// - /// Takes in the rebalanced child index, as well as both halves of the rebalanced child and splits the children, inserting the left and right child halves appropriately - /// - /// For more context, see the documentation for the splitArrayAndInsertTwo method in ArrayUtils.mo - public func splitChildrenInTwoWithRebalances( - children : [var ?Node], - rebalancedChildIndex : Nat, - leftChildInsert : Node, - rightChildInsert : Node - ) : ([var ?Node], [var ?Node]) { - BTreeHelper.splitArrayAndInsertTwo>(children, rebalancedChildIndex, leftChildInsert, rightChildInsert) - }; - - /// Helper used to get the element index of of a element within a node - /// - /// for more, see the BinarySearch.binarySearchNode() documentation - public func getElementIndex(data : Data, compare : (T, T) -> Order.Order, element : T) : BinarySearch.SearchResult { - BinarySearch.binarySearchNode(data.elements, compare, element, data.count) - }; - - // calculates a BTree Node's minimum allowed elements given the order of the BTree - public func minElementsFromOrder(order : Nat) : Nat { - if (order % 2 == 0) { order / 2 - 1 } else { order / 2 } - }; - - // Given a node, get the maximum element (right most leaf element) - public func getMaxElement(node : ?Node) : T { - switch (node) { - case (?#leaf({ data })) { - switch (data.elements[data.count - 1]) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.NodeUtil.getMaxElement, data cannot have more elements than it's count") - }; - case (?element) { element } - } - }; - case (?#internal({ data; children })) { - getMaxElement(children[data.count]) - }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.NodeUtil.getMaxElement, the node provided cannot be null") - } - } - }; - - type InorderBorrowType = { - #predecessor; - #successor - }; - - // attempts to retrieve the in max element of the child leaf node directly to the left if the node will allow it - // returns the deleted max element if able to retrieve, null if not able - // - // mutates the predecessing node's elements - public func borrowFromLeftLeafChild(children : [var ?Node], ofChildIndex : Nat) : ?T { - let predecessorIndex : Nat = ofChildIndex - 1; - borrowFromLeafChild(children, predecessorIndex, #predecessor) - }; - - // attempts to retrieve the in max element of the child leaf node directly to the right if the node will allow it - // returns the deleted max element if able to retrieve, null if not able - // - // mutates the predecessing node's elements - public func borrowFromRightLeafChild(children : [var ?Node], ofChildIndex : Nat) : ?T { - borrowFromLeafChild(children, ofChildIndex + 1, #successor) - }; - - func borrowFromLeafChild(children : [var ?Node], borrowChildIndex : Nat, childSide : InorderBorrowType) : ?T { - let minElements = minElementsFromOrder(children.size()); - - switch (children[borrowChildIndex]) { - case (?#leaf({ data })) { - if (data.count > minElements) { - // able to borrow an element from this child, so decrement the count of elements - data.count -= 1; // Since enforce order >= 4, there will always be at least 1 element per node - switch (childSide) { - case (#predecessor) { - let deletedElement = data.elements[data.count]; - data.elements[data.count] := null; - deletedElement - }; - case (#successor) { - ?BTreeHelper.deleteAndShift(data.elements, 0) - } - } - } else { null } - }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.NodeUtil.borrowFromLeafChild, the node at the borrow child index cannot be null or internal") - } - } - }; - - type InternalBorrowResult = { - #borrowed : InternalBorrow; - #notEnoughElements : Internal - }; - - type InternalBorrow = { - deletedSiblingElement : ?T; - child : ?Node - }; - - // Attempts to borrow an element and child from an internal sibling node - public func borrowFromInternalSibling(children : [var ?Node], borrowChildIndex : Nat, borrowType : InorderBorrowType) : InternalBorrowResult { - let minElements = minElementsFromOrder(children.size()); - - switch (children[borrowChildIndex]) { - case (?#internal({ data; children })) { - if (data.count > minElements) { - data.count -= 1; - switch (borrowType) { - case (#predecessor) { - let deletedSiblingElement = data.elements[data.count]; - data.elements[data.count] := null; - let child = children[data.count + 1]; - children[data.count + 1] := null; - #borrowed({ - deletedSiblingElement; - child - }) - }; - case (#successor) { - #borrowed({ - deletedSiblingElement = ?BTreeHelper.deleteAndShift(data.elements, 0); - child = ?BTreeHelper.deleteAndShift(children, 0) - }) - } - } - } else { #notEnoughElements({ data; children }) } - }; - case _ { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In Set.NodeUtil.borrowFromInternalSibling from internal sibling, the child at the borrow index cannot be null or a leaf") - } - } - }; - - type SiblingSide = { #left; #right }; - - // Rotates the borrowed elements and child from sibling side of the internal node to the internal child recipient - public func rotateBorrowedElementsAndChildFromSibling( - internalNode : Internal, - parentRotateIndex : Nat, - borrowedSiblingElement : ?T, - borrowedSiblingChild : ?Node, - internalChildRecipient : Internal, - siblingSide : SiblingSide - ) { - // if borrowing from the left, the rotated element and child will always be inserted first - // if borrowing from the right, the rotated element and child will always be inserted last - let (elementIndex, childIndex) = switch (siblingSide) { - case (#left) { (0, 0) }; - case (#right) { - (internalChildRecipient.data.count, internalChildRecipient.data.count + 1) - } - }; - - // get the parent element that will be pushed down the the child - let elementToBePushedToChild = internalNode.data.elements[parentRotateIndex]; - // replace the parent with the sibling element - internalNode.data.elements[parentRotateIndex] := borrowedSiblingElement; - // push the element and child down into the internalChild - insertAtIndexOfNonFullNodeData(internalChildRecipient.data, elementToBePushedToChild, elementIndex); - - BTreeHelper.insertAtPosition>(internalChildRecipient.children, borrowedSiblingChild, childIndex, internalChildRecipient.data.count) - }; - - // Merges the elements and children of two internal nodes, pushing the parent element in between the right and left halves - public func mergeChildrenAndPushDownParent(leftChild : Internal, parentElement : ?T, rightChild : Internal) : Internal { - { - data = mergeData(leftChild.data, parentElement, rightChild.data); - children = mergeChildren(leftChild.children, rightChild.children) - } - }; - - func mergeData(leftData : Data, parentElement : ?T, rightData : Data) : Data { - assert leftData.count <= minElementsFromOrder(leftData.elements.size() + 1); - assert rightData.count <= minElementsFromOrder(rightData.elements.size() + 1); - - let mergedElements = VarArray.repeat(null, leftData.elements.size()); - var i = 0; - while (i < leftData.count) { - mergedElements[i] := leftData.elements[i]; - i += 1 - }; - - mergedElements[i] := parentElement; - i += 1; - - var j = 0; - while (j < rightData.count) { - mergedElements[i] := rightData.elements[j]; - i += 1; - j += 1 - }; - - { - elements = mergedElements; - var count = leftData.count + 1 + rightData.count - } - }; - - func mergeChildren(leftChildren : [var ?Node], rightChildren : [var ?Node]) : [var ?Node] { - let mergedChildren = VarArray.repeat>(null, leftChildren.size()); - var i = 0; - - while (Option.isSome(leftChildren[i])) { - mergedChildren[i] := leftChildren[i]; - i += 1 - }; - - var j = 0; - while (Option.isSome(rightChildren[j])) { - mergedChildren[i] := rightChildren[j]; - i += 1; - j += 1 - }; - - mergedChildren - } - } -} diff --git a/.mops/core@2.5.0/src/Stack.mo b/.mops/core@2.5.0/src/Stack.mo deleted file mode 100644 index 89c099b..0000000 --- a/.mops/core@2.5.0/src/Stack.mo +++ /dev/null @@ -1,879 +0,0 @@ -/// A mutable stack data structure. -/// Elements can be pushed on top of the stack -/// and removed from top of the stack (LIFO). -/// -/// Example: -/// ```motoko -/// import Stack "mo:core/Stack"; -/// import Debug "mo:core/Debug"; -/// -/// persistent actor { -/// let levels = Stack.empty(); -/// Stack.push(levels, "Inner"); -/// Stack.push(levels, "Middle"); -/// Stack.push(levels, "Outer"); -/// assert Stack.pop(levels) == ?"Outer"; -/// assert Stack.pop(levels) == ?"Middle"; -/// assert Stack.pop(levels) == ?"Inner"; -/// assert Stack.pop(levels) == null; -/// } -/// ``` -/// -/// The internal implementation is a singly-linked list. -/// -/// Performance: -/// * Runtime: `O(1)` for push, pop, and peek operation. -/// * Space: `O(n)`. -/// `n` denotes the number of elements stored on the stack. - -// TODO: optimize or re-use pure/List operations (e.g. for `any` etc) - -import Order "Order"; -import Iter "Iter"; -import Types "Types"; -import PureList "pure/List"; - -module { - type List = Types.Pure.List; - public type Stack = Types.Stack; - - /// Convert a mutable stack to an immutable, purely functional list. - /// Please note that functional lists are ordered like stacks (FIFO). - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import PureList "mo:core/pure/List"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let mutableStack = Stack.empty(); - /// Stack.push(mutableStack, 3); - /// Stack.push(mutableStack, 2); - /// Stack.push(mutableStack, 1); - /// let immutableList = Stack.toPure(mutableStack); - /// assert Iter.toArray(PureList.values(immutableList)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the stack. - /// @deprecated M0235 - public func toPure(self : Stack) : PureList.List { - self.top - }; - - public func toArray(self : Stack) : [T] { - Iter.toArray(values(self)) - }; - - public func toVarArray(self : Stack) : [var T] { - Iter.toVarArray(values(self)) - }; - - /// Convert an immutable, purely functional list to a mutable stack. - /// Please note that functional lists are ordered like stacks (FIFO). - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import PureList "mo:core/pure/List"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let immutableList = PureList.fromIter([1, 2, 3].values()); - /// let mutableStack = Stack.fromPure(immutableList); - /// assert Iter.toArray(Stack.values(mutableStack)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the queue. - /// @deprecated M0235 - public func fromPure(list : PureList.List) : Stack { - var size = 0; - var cur = list; - loop { - switch cur { - case (?(_, next)) { - size += 1; - cur := next - }; - case null { - return { var top = list; var size } - } - } - } - }; - - public func fromVarArray(array : [var T]) : Stack { - fromIter(array.values()) - }; - - public func fromArray(array : [T]) : Stack { - fromIter(array.values()) - }; - - /// Create a new empty mutable stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// assert Stack.size(stack) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Stack { - { - var top = null; - var size = 0 - } - }; - - /// Creates a new stack with `size` elements by applying the `generator` function to indices `[0..size-1]`. - /// Elements are pushed in ascending index order. - /// Which means that the generated element with the index `0` will be at the bottom of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let stack = Stack.tabulate(3, func(i) { 2 * i }); - /// assert Iter.toArray(Stack.values(stack)) == [4, 2, 0]; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `generator` has O(1) costs. - public func tabulate(size : Nat, generator : Nat -> T) : Stack { - let stack = empty(); - var index = 0; - while (index < size) { - let element = generator(index); - push(stack, element); - index += 1 - }; - stack - }; - - /// Creates a new stack containing a single element. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.singleton("motoko"); - /// assert Stack.peek(stack) == ?"motoko"; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func singleton(element : T) : Stack { - let stack = empty(); - push(stack, element); - stack - }; - - /// Removes all elements from the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// Stack.clear(stack); - /// assert Stack.isEmpty(stack); - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func clear(self : Stack) { - self.top := null; - self.size := 0 - }; - - /// Creates a deep copy of the stack with the same elements in the same order. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let original = Stack.fromIter([3, 2, 1].values()); - /// let copy = Stack.clone(original); - /// assert Stack.equal(copy, original, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack. - public func clone(self : Stack) : Stack { - let copy = empty(); - for (element in values(self)) { - push(copy, element) - }; - reverse(copy); - copy - }; - - /// Returns true if the stack contains no elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// assert Stack.isEmpty(stack); - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func isEmpty(self : Stack) : Bool { - self.size == 0 - }; - - /// Returns the number of elements on the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.size(stack) == 3; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func size(self : Stack) : Nat { - self.size - }; - - /// Returns true if the stack contains the specified element. - /// Uses the provided equality function to compare elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.contains(stack, Nat.equal, 2); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and assuming - /// that `equal` has O(1) costs. - public func contains(self : Stack, equal : (implicit : (T, T) -> Bool), element : T) : Bool { - for (existing in values(self)) { - if (equal(existing, element)) { - return true - } - }; - false - }; - - public func reverseValues(self : Stack) : Iter.Iter { - Iter.reverse(values(self)) - }; - - /// Pushes a new element onto the top of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 42); - /// assert Stack.peek(stack) == ?42; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func push(self : Stack, value : T) { - self.top := ?(value, self.top); - self.size += 1 - }; - - /// Returns the top element of the stack without removing it. - /// Returns null if the stack is empty. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// assert Stack.peek(stack) == ?1; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func peek(self : Stack) : ?T { - switch (self.top) { - case null null; - case (?(value, _)) ?value - } - }; - - /// Removes and returns the top element of the stack. - /// Returns null if the stack is empty. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// assert Stack.pop(stack) == ?1; - /// assert Stack.pop(stack) == ?2; - /// assert Stack.pop(stack) == ?3; - /// assert Stack.pop(stack) == null; - /// } - /// ``` - /// - /// Runtime: O(1) - /// Space: O(1) - public func pop(self : Stack) : ?T { - switch (self.top) { - case null null; - case (?(value, next)) { - self.top := next; - self.size -= 1; - ?value - } - } - }; - - /// Returns the element at the specified position from the top of the stack. - /// Returns null if position is out of bounds. - /// Position 0 is the top of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 'c'); - /// Stack.push(stack, 'b'); - /// Stack.push(stack, 'a'); - /// assert Stack.get(stack, 0) == ?'a'; - /// assert Stack.get(stack, 1) == ?'b'; - /// assert Stack.get(stack, 2) == ?'c'; - /// assert Stack.get(stack, 3) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack. - public func get(self : Stack, position : Nat) : ?T { - var index = 0; - var current = self.top; - while (index < position) { - switch (current) { - case null return null; - case (?(_, next)) { - current := next - } - }; - index += 1 - }; - switch (current) { - case null null; - case (?(value, _)) ?value - } - }; - - /// Reverses the order of elements in the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// Stack.reverse(stack); - /// assert Stack.pop(stack) == ?3; - /// assert Stack.pop(stack) == ?2; - /// assert Stack.pop(stack) == ?1; - /// assert Stack.pop(stack) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack. - public func reverse(self : Stack) { - var last : List = null; - for (element in values(self)) { - last := ?(element, last) - }; - self.top := last - }; - - /// Returns an iterator over the elements in the stack, from top to bottom. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// assert Iter.toArray(Stack.values(stack)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: O(1) for iterator creation, O(n) for full traversal - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack. - public func values(self : Stack) : Types.Iter { - object { - var current = self.top; - - public func next() : ?T { - switch (current) { - case null null; - case (?(value, next)) { - current := next; - ?value - } - } - } - } - }; - - /// Returns true if all elements in the stack satisfy the predicate. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([2, 4, 6].values()); - /// assert Stack.all(stack, func(n) = n % 2 == 0); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `predicate` has O(1) costs. - public func all(self : Stack, predicate : T -> Bool) : Bool { - for (element in values(self)) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Returns true if any element in the stack satisfies the predicate. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.any(stack, func(n) = n == 2); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming `predicate` has O(1) costs. - public func any(self : Stack, predicate : T -> Bool) : Bool { - for (element in values(self)) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Applies the operation to each element in the stack, from top to bottom. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// import Debug "mo:core/Debug"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// var text = ""; - /// Stack.forEach(stack, func(n) = text #= Nat.toText(n)); - /// assert text == "123"; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `operation` has O(1) costs. - public func forEach(self : Stack, operation : T -> ()) { - for (element in values(self)) { - operation(element) - } - }; - - /// Creates a new stack by applying the projection function to each element. - /// Maintains the original order of elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// let doubled = Stack.map(stack, func(n) { 2 * n }); - /// assert Stack.get(doubled, 0) == ?2; - /// assert Stack.get(doubled, 1) == ?4; - /// assert Stack.get(doubled, 2) == ?6; - /// assert Stack.get(doubled, 3) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `project` has O(1) costs. - public func map(self : Stack, project : T -> U) : Stack { - let result = empty(); - for (element in values(self)) { - push(result, project(element)) - }; - reverse(result); - result - }; - - /// Creates a new stack containing only elements that satisfy the predicate. - /// Maintains the relative order of elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 4); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// let evens = Stack.filter(stack, func(n) { n % 2 == 0 }); - /// assert Stack.pop(evens) == ?2; - /// assert Stack.pop(evens) == ?4; - /// assert Stack.pop(evens) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming `predicate` has O(1) costs. - public func filter(self : Stack, predicate : T -> Bool) : Stack { - let result = empty(); - for (element in values(self)) { - if (predicate(element)) { - push(result, element) - } - }; - reverse(result); - result - }; - - /// Creates a new stack by applying the projection function to each element - /// and keeping only the successful results (where project returns ?value). - /// Maintains the relative order of elements. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.empty(); - /// Stack.push(stack, 4); - /// Stack.push(stack, 3); - /// Stack.push(stack, 2); - /// Stack.push(stack, 1); - /// let evenDoubled = Stack.filterMap(stack, func(n) { - /// if (n % 2 == 0) { - /// ?(n * 2) - /// } else { - /// null - /// } - /// }); - /// assert Stack.pop(evenDoubled) == ?4; - /// assert Stack.pop(evenDoubled) == ?8; - /// assert Stack.pop(evenDoubled) == null; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `project` has O(1) costs. - public func filterMap(self : Stack, project : T -> ?U) : Stack { - let result = empty(); - for (element in values(self)) { - switch (project(element)) { - case null {}; - case (?newElement) { - push(result, newElement) - } - } - }; - reverse(result); - result - }; - - /// Return the first element for which the given `predicate` is true, - /// if such an element exists. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromPure(?(1, ?(2, ?(3, null)))); - /// assert Stack.find(stack, func n = n > 1) == ?2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - - public func find(self : Stack, predicate : T -> Bool) : ?T = PureList.find(self.top, predicate); - - /// Return the first index for which the given `predicate` is true. - /// If no element satisfies the predicate, returns null. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// - /// persistent actor { - /// let stack = Stack.fromPure(?('A', ?('B', ?('C', ?('D', null))))); - /// let found = Stack.findIndex(stack, func x = x == 'C'); - /// assert found == ?2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func findIndex(self : Stack, predicate : T -> Bool) : ?Nat = PureList.findIndex(self.top, predicate); - - /// Compares two stacks for equality using the provided equality function. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack1 = Stack.fromIter([3, 2, 1].values()); - /// let stack2 = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.equal(stack1, stack2, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `equal` has O(1) costs. - public func equal(self : Stack, other : Stack, equal : (implicit : (T, T) -> Bool)) : Bool { - if (size(self) != size(other)) { - return false - }; - let iterator1 = values(self); - let iterator2 = values(other); - loop { - let element1 = iterator1.next(); - let element2 = iterator2.next(); - switch (element1, element2) { - case (null, null) { - return true - }; - case (?element1, ?element2) { - if (not equal(element1, element2)) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Creates a new stack from an iterator. - /// Elements are pushed in iteration order. Which means that the last element - /// of the iterator will be the first element on top of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Iter.toArray(Stack.values(stack)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of iterated elements. - public func fromIter(iter : Types.Iter) : Stack { - let stack = empty(); - for (element in iter) { - push(stack, element) - }; - stack - }; - - /// Convert an iterator into a stack. - /// Elements are pushed in iteration order. Which means that the last element - /// of the iterator will be the first element on top of the stack. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = [3, 2, 1].values(); - /// - /// let stack = iter.toStack(); - /// - /// assert Iter.toArray(Stack.values(stack)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of iterated elements. - public func toStack(self : Types.Iter) : Stack { - fromIter(self) - }; - - /// Converts the stack to its string representation using the provided - /// element formatting function. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.toText(stack, Nat.toText) == "Stack[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(n) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `format` has O(1) costs. - public func toText(self : Stack, format : (implicit : (toText : T -> Text))) : Text { - var text = "Stack["; - var sep = ""; - for (element in values(self)) { - text #= sep # format(element); - sep := ", " - }; - text #= "]"; - text - }; - - /// Compares two stacks lexicographically using the provided comparison function. - /// - /// Example: - /// ```motoko - /// import Stack "mo:core/Stack"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let stack1 = Stack.fromIter([2, 1].values()); - /// let stack2 = Stack.fromIter([3, 2, 1].values()); - /// assert Stack.compare(stack1, stack2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: O(n) - /// Space: O(1) - /// where `n` denotes the number of elements stored on the stack and - /// assuming that `compare` has O(1) costs. - public func compare(self : Stack, other : Stack, compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let iterator1 = values(self); - let iterator2 = values(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?element1, ?element2) { - let comparison = compare(element1, element2); - if (comparison != #equal) { - return comparison - } - } - } - } - } -} diff --git a/.mops/core@2.5.0/src/Text.mo b/.mops/core@2.5.0/src/Text.mo deleted file mode 100644 index 1f6c8a5..0000000 --- a/.mops/core@2.5.0/src/Text.mo +++ /dev/null @@ -1,967 +0,0 @@ -/// Utility functions for `Text` values. -/// -/// A `Text` value represents human-readable text as a sequence of characters of type `Char`. -/// -/// ```motoko -/// let text = "Hello!"; -/// let size = text.size(); -/// assert size == 6; -/// let iter = text.chars(); -/// assert iter.next() == ?'H'; -/// assert iter.next() == ?'e'; -/// assert iter.next() == ?'l'; -/// assert iter.next() == ?'l'; -/// assert iter.next() == ?'o'; -/// assert iter.next() == ?'!'; -/// assert iter.next() == null; -/// let concat = text # " 👋"; -/// assert concat == "Hello! 👋"; -/// ``` -/// -/// The `"mo:core/Text"` module defines additional operations on `Text` values. -/// -/// Import the module from the core package: -/// -/// ```motoko name=import -/// import Text "mo:core/Text"; -/// ``` -/// -/// Note: `Text` values are represented as ropes of UTF-8 character sequences with O(1) concatenation. -/// - -import Char "Char"; -import Iter "Iter"; -import Stack "Stack"; -import Types "Types"; -import Prim "mo:⛔"; -import Order "Order"; - -module { - - /// The type corresponding to primitive `Text` values. - /// - /// ```motoko - /// let hello = "Hello!"; - /// let emoji = "👋"; - /// let concat = hello # " " # emoji; - /// assert concat == "Hello! 👋"; - /// ``` - public type Text = Prim.Types.Text; - - /// Converts the given `Char` to a `Text` value. - /// - /// ```motoko include=import - /// let text = Text.fromChar('A'); - /// assert text == "A"; - /// ``` - public let fromChar : (c : Char) -> Text = Prim.charToText; - - /// Converts the given `[Char]` to a `Text` value. - /// - /// ```motoko include=import - /// let text = Text.fromArray(['A', 'v', 'o', 'c', 'a', 'd', 'o']); - /// assert text == "Avocado"; - /// ``` - /// - /// Runtime: O(a.size()) - /// Space: O(a.size()) - public func fromArray(a : [Char]) : Text = fromIter(a.vals()); - - /// Converts the given `[var Char]` to a `Text` value. - /// - /// ```motoko include=import - /// let text = Text.fromVarArray([var 'E', 'g', 'g', 'p', 'l', 'a', 'n', 't']); - /// assert text == "Eggplant"; - /// ``` - /// - /// Runtime: O(a.size()) - /// Space: O(a.size()) - public func fromVarArray(a : [var Char]) : Text = fromIter(a.vals()); - - /// Iterates over each `Char` value in the given `Text`. - /// - /// Equivalent to calling the `t.chars()` method where `t` is a `Text` value. - /// - /// ```motoko include=import - /// let chars = Text.toIter("abc"); - /// assert chars.next() == ?'a'; - /// assert chars.next() == ?'b'; - /// assert chars.next() == ?'c'; - /// assert chars.next() == null; - /// ``` - public func toIter(self : Text) : Iter.Iter = self.chars(); - - /// Collapses the characters in `text` into a single value by starting with `base` - /// and progessively combining characters into `base` with `combine`. Iteration runs - /// left to right. - /// - /// ```motoko include=import - /// - /// let text = "Mississippi"; - /// let count = - /// Text.foldLeft( - /// text, - /// 0, // start the sum at 0 - /// func(ss, c) = if (c == 's') ss + 1 else ss - /// ); - /// assert count == 4; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(self : Text, base : A, combine : (A, Char) -> A) : A { - var acc = base; - for (c in self.chars()) acc := combine(acc, c); - acc - }; - - /// Creates a new `Array` containing characters of the given `Text`. - /// - /// Equivalent to `Iter.toArray(t.chars())`. - /// - /// ```motoko include=import - /// assert Text.toArray("Café") == ['C', 'a', 'f', 'é']; - /// ``` - /// - /// Runtime: O(t.size()) - /// Space: O(t.size()) - public func toArray(self : Text) : [Char] { - let cs = self.chars(); - // We rely on Array_tabulate's implementation details: it fills - // the array from left to right sequentially. - Prim.Array_tabulate( - self.size(), - func _ { - switch (cs.next()) { - case (?c) { c }; - case null { Prim.trap("Text.toArray()") } - } - } - ) - }; - - /// Creates a new mutable `Array` containing characters of the given `Text`. - /// - /// Equivalent to `Iter.toArrayMut(t.chars())`. - /// - /// ```motoko include=import - /// import VarArray "mo:core/VarArray"; - /// import Char "mo:core/Char"; - /// - /// assert VarArray.equal(Text.toVarArray("Café"), [var 'C', 'a', 'f', 'é'], Char.equal); - /// ``` - /// - /// Runtime: O(t.size()) - /// Space: O(t.size()) - public func toVarArray(self : Text) : [var Char] { - let n = self.size(); - if (n == 0) { - return [var] - }; - let array = Prim.Array_init(n, ' '); - var i = 0; - for (c in self.chars()) { - array[i] := c; - i += 1 - }; - array - }; - - /// Creates a `Text` value from a `Char` iterator. - /// - /// ```motoko include=import - /// let text = Text.fromIter(['a', 'b', 'c'].values()); - /// assert text == "abc"; - /// ``` - public func fromIter(cs : Iter.Iter) : Text { - var r = ""; - for (c in cs) { - r #= Prim.charToText(c) - }; - return r - }; - - /// Returns whether the given `Text` is empty (has a size of zero). - /// - /// ```motoko include=import - /// let text1 = ""; - /// let text2 = "example"; - /// assert Text.isEmpty(text1); - /// assert not Text.isEmpty(text2); - /// ``` - public func isEmpty(self : Text) : Bool = self == ""; - - /// Returns the number of characters in the given `Text`. - /// - /// Equivalent to calling `t.size()` where `t` is a `Text` value. - /// - /// ```motoko include=import - /// let size = Text.size("abc"); - /// assert size == 3; - /// ``` - public func size(self : Text) : Nat = self.size(); - - /// Returns `t1 # t2`, where `#` is the `Text` concatenation operator. - /// - /// ```motoko include=import - /// let a = "Hello"; - /// let b = "There"; - /// let together = a # b; - /// assert together == "HelloThere"; - /// let withSpace = a # " " # b; - /// assert withSpace == "Hello There"; - /// let togetherAgain = Text.concat(a, b); - /// assert togetherAgain == "HelloThere"; - /// ``` - public func concat(self : Text, other : Text) : Text = self # other; - - /// Returns a new `Text` with the characters of the input `Text` in reverse order. - /// - /// ```motoko include=import - /// let text = Text.reverse("Hello"); - /// assert text == "olleH"; - /// ``` - /// - /// Runtime: O(t.size()) - /// Space: O(t.size()) - public func reverse(self : Text) : Text { - fromIter(Iter.reverse(self.chars())) - }; - - /// Returns true if two text values are equal. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.equal("hello", "hello"); - /// assert not Text.equal("hello", "world"); - /// ``` - public func equal(self : Text, other : Text) : Bool { self == other }; - - /// Returns true if two text values are not equal. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.notEqual("hello", "world"); - /// assert not Text.notEqual("hello", "hello"); - /// ``` - public func notEqual(self : Text, other : Text) : Bool { self != other }; - - /// Returns true if the first text value is lexicographically less than the second. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.less("apple", "banana"); - /// assert not Text.less("banana", "apple"); - /// ``` - public func less(self : Text, other : Text) : Bool { self < other }; - - /// Returns true if the first text value is lexicographically less than or equal to the second. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.lessOrEqual("apple", "banana"); - /// assert Text.lessOrEqual("apple", "apple"); - /// assert not Text.lessOrEqual("banana", "apple"); - /// ``` - public func lessOrEqual(self : Text, other : Text) : Bool { self <= other }; - - /// Returns true if the first text value is lexicographically greater than the second. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.greater("banana", "apple"); - /// assert not Text.greater("apple", "banana"); - /// ``` - public func greater(self : Text, other : Text) : Bool { self > other }; - - /// Returns true if the first text value is lexicographically greater than or equal to the second. - /// - /// ```motoko - /// import Text "mo:core/Text"; - /// - /// assert Text.greaterOrEqual("banana", "apple"); - /// assert Text.greaterOrEqual("apple", "apple"); - /// assert not Text.greaterOrEqual("apple", "banana"); - /// ``` - public func greaterOrEqual(self : Text, other : Text) : Bool { self >= other }; - - /// Compares `t1` and `t2` lexicographically. - /// - /// ```motoko include=import - /// assert Text.compare("abc", "abc") == #equal; - /// assert Text.compare("abc", "def") == #less; - /// assert Text.compare("abc", "ABC") == #greater; - /// ``` - public func compare(self : Text, other : Text) : Order.Order { - let c = Prim.textCompare(self, other); - if (c < 0) #less else if (c == 0) #equal else #greater - }; - - private func extract(self : Text, i : Nat, j : Nat) : Text { - let size = self.size(); - if (i == 0 and j == size) return self; - assert (j <= size); - let cs = self.chars(); - var r = ""; - var n = i; - while (n > 0) { - ignore cs.next(); - n -= 1 - }; - n := j; - while (n > 0) { - switch (cs.next()) { - case null { assert false }; - case (?c) { r #= Prim.charToText(c) } - }; - n -= 1 - }; - return r - }; - - /// Join an iterator of `Text` values with a given delimiter. - /// - /// ```motoko include=import - /// let joined = Text.join(["a", "b", "c"].values(), ", "); - /// assert joined == "a, b, c"; - /// ``` - public func join(self : Iter.Iter, sep : Text) : Text { - var r = ""; - if (sep.size() == 0) { - for (t in self) { - r #= t - }; - return r - }; - let next = self.next; - switch (next()) { - case null { return r }; - case (?t) { - r #= t - } - }; - loop { - switch (next()) { - case null { return r }; - case (?t) { - r #= sep; - r #= t - } - } - } - }; - - /// Applies a function to each character in a `Text` value, returning the concatenated `Char` results. - /// - /// ```motoko include=import - /// // Replace all occurrences of '?' with '!' - /// let result = Text.map("Motoko?", func(c) { - /// if (c == '?') '!' - /// else c - /// }); - /// assert result == "Motoko!"; - /// ``` - public func map(self : Text, f : Char -> Char) : Text { - var r = ""; - for (c in self.chars()) { - r #= Prim.charToText(f(c)) - }; - r - }; - - /// Returns the result of applying `f` to each character in `ts`, concatenating the intermediate text values. - /// - /// ```motoko include=import - /// // Replace all occurrences of '?' with "!!" - /// let result = Text.flatMap("Motoko?", func(c) { - /// if (c == '?') "!!" - /// else Text.fromChar(c) - /// }); - /// assert result == "Motoko!!"; - /// ``` - public func flatMap(self : Text, f : Char -> Text) : Text { - var r = ""; - for (c in self.chars()) { - r #= f(c) - }; - r - }; - - /// A pattern `p` describes a sequence of characters. A pattern has one of the following forms: - /// - /// * `#char c` matches the single character sequence, `c`. - /// * `#text t` matches multi-character text sequence `t`. - /// * `#predicate p` matches any single character sequence `c` satisfying predicate `p(c)`. - /// - /// A _match_ for `p` is any sequence of characters matching the pattern `p`. - /// - /// ```motoko include=import - /// let charPattern = #char 'A'; - /// let textPattern = #text "phrase"; - /// let predicatePattern : Text.Pattern = #predicate (func(c) { c == 'A' or c == 'B' }); - /// assert Text.contains("A", predicatePattern); - /// assert Text.contains("B", predicatePattern); - /// ``` - public type Pattern = Types.Pattern; - - private func take(n : Nat, cs : Iter.Iter) : Iter.Iter { - var i = n; - object { - public func next() : ?Char { - if (i == 0) return null; - i -= 1; - return cs.next() - } - } - }; - - private func empty() : Iter.Iter { - object { - public func next() : ?Char = null - } - }; - - private type Match = { - /// #success on complete match - #success; - /// #fail(cs,c) on partial match of cs, but failing match on c - #fail : (cs : Iter.Iter, c : Char); - /// #empty(cs) on partial match of cs and empty stream - #empty : (cs : Iter.Iter) - }; - - private func sizeOfPattern(pat : Pattern) : Nat { - switch pat { - case (#text(t)) { t.size() }; - case (#predicate(_) or #char(_)) { 1 } - } - }; - - private func matchOfPattern(pat : Pattern) : (cs : Iter.Iter) -> Match { - switch pat { - case (#char(p)) { - func(cs : Iter.Iter) : Match { - switch (cs.next()) { - case (?c) { - if (p == c) { - #success - } else { - #fail(empty(), c) - } - }; - case null { #empty(empty()) } - } - } - }; - case (#predicate(p)) { - func(cs : Iter.Iter) : Match { - switch (cs.next()) { - case (?c) { - if (p(c)) { - #success - } else { - #fail(empty(), c) - } - }; - case null { #empty(empty()) } - } - } - }; - case (#text(p)) { - func(cs : Iter.Iter) : Match { - var i = 0; - let ds = p.chars(); - loop { - switch (ds.next()) { - case (?d) { - switch (cs.next()) { - case (?c) { - if (c != d) { - return #fail(take(i, p.chars()), c) - }; - i += 1 - }; - case null { - return #empty(take(i, p.chars())) - } - } - }; - case null { return #success } - } - } - } - } - } - }; - - private class CharBuffer(cs : Iter.Iter) : Iter.Iter = { - - var stack : Stack.Stack<(Iter.Iter, Char)> = Stack.empty(); - - public func pushBack(cs0 : Iter.Iter, c : Char) { - Stack.push(stack, (cs0, c)) - }; - - public func next() : ?Char { - switch (Stack.peek(stack)) { - case (?(buff, c)) { - switch (buff.next()) { - case null { - ignore Stack.pop(stack); - return ?c - }; - case oc { - return oc - } - } - }; - case null { - return cs.next() - } - } - } - }; - - /// Splits the input `Text` with the specified `Pattern`. - /// - /// Two fields are separated by exactly one match. - /// - /// ```motoko include=import - /// let words = Text.split("This is a sentence.", #char ' '); - /// assert Text.join(words, "|") == "This|is|a|sentence."; - /// ``` - public func split(self : Text, p : Pattern) : Iter.Iter { - let match = matchOfPattern(p); - let cs = CharBuffer(self.chars()); - var state = 0; - var field = ""; - object { - public func next() : ?Text { - switch state { - case (0 or 1) { - loop { - switch (match(cs)) { - case (#success) { - let r = field; - field := ""; - state := 1; - return ?r - }; - case (#empty(cs1)) { - for (c in cs1) { - field #= fromChar(c) - }; - let r = if (state == 0 and field == "") { - null - } else { - ?field - }; - state := 2; - return r - }; - case (#fail(cs1, c)) { - cs.pushBack(cs1, c); - switch (cs.next()) { - case (?ci) { - field #= fromChar(ci) - }; - case null { - let r = if (state == 0 and field == "") { - null - } else { - ?field - }; - state := 2; - return r - } - } - } - } - } - }; - case _ { return null } - } - } - } - }; - - /// Returns a sequence of tokens from the input `Text` delimited by the specified `Pattern`, derived from start to end. - /// A "token" is a non-empty maximal subsequence of `t` not containing a match for pattern `p`. - /// Two tokens may be separated by one or more matches of `p`. - /// - /// ```motoko include=import - /// let tokens = Text.tokens("this needs\n an example", #predicate (func(c) { c == ' ' or c == '\n' })); - /// assert Text.join(tokens, "|") == "this|needs|an|example"; - /// ``` - public func tokens(self : Text, p : Pattern) : Iter.Iter { - let fs = split(self, p); - object { - public func next() : ?Text { - switch (fs.next()) { - case (?"") { next() }; - case ot { ot } - } - } - } - }; - - /// Returns `true` if the input `Text` contains a match for the specified `Pattern`. - /// - /// ```motoko include=import - /// assert Text.contains("Motoko", #text "oto"); - /// assert not Text.contains("Motoko", #text "xyz"); - /// ``` - public func contains(self : Text, p : Pattern) : Bool { - let match = matchOfPattern(p); - let cs = CharBuffer(self.chars()); - loop { - switch (match(cs)) { - case (#success) { - return true - }; - case (#empty(_cs1)) { - return false - }; - case (#fail(cs1, c)) { - cs.pushBack(cs1, c); - switch (cs.next()) { - case null { - return false - }; - case _ {}; // continue - } - } - } - } - }; - - /// Returns `true` if the input `Text` starts with a prefix matching the specified `Pattern`. - /// - /// ```motoko include=import - /// assert Text.startsWith("Motoko", #text "Mo"); - /// ``` - public func startsWith(self : Text, p : Pattern) : Bool { - var cs = self.chars(); - let match = matchOfPattern(p); - switch (match(cs)) { - case (#success) { true }; - case _ { false } - } - }; - - /// Returns `true` if the input `Text` ends with a suffix matching the specified `Pattern`. - /// - /// ```motoko include=import - /// assert Text.endsWith("Motoko", #char 'o'); - /// ``` - public func endsWith(self : Text, p : Pattern) : Bool { - let s2 = sizeOfPattern(p); - if (s2 == 0) return true; - let s1 = self.size(); - if (s2 > s1) return false; - let match = matchOfPattern(p); - var cs1 = self.chars(); - var diff : Nat = s1 - s2; - while (diff > 0) { - ignore cs1.next(); - diff -= 1 - }; - switch (match(cs1)) { - case (#success) { true }; - case _ { false } - } - }; - - /// Returns the input text `t` with all matches of pattern `p` replaced by text `r`. - /// - /// ```motoko include=import - /// let result = Text.replace("abcabc", #char 'a', "A"); - /// assert result == "AbcAbc"; - /// ``` - public func replace(self : Text, p : Pattern, r : Text) : Text { - let match = matchOfPattern(p); - let size = sizeOfPattern(p); - let cs = CharBuffer(self.chars()); - var res = ""; - label l loop { - switch (match(cs)) { - case (#success) { - res #= r; - if (size > 0) { - continue l - } - }; - case (#empty(cs1)) { - for (c1 in cs1) { - res #= fromChar(c1) - }; - break l - }; - case (#fail(cs1, c)) { - cs.pushBack(cs1, c) - } - }; - switch (cs.next()) { - case null { - break l - }; - case (?c1) { - res #= fromChar(c1) - }; // continue - } - }; - return res - }; - - /// Strips one occurrence of the given `Pattern` from the beginning of the input `Text`. - /// If you want to remove multiple instances of the pattern, use `Text.trimStart()` instead. - /// - /// ```motoko include=import - /// // Try to strip a nonexistent character - /// let none = Text.stripStart("abc", #char '-'); - /// assert none == null; - /// // Strip just one '-' - /// let one = Text.stripStart("--abc", #char '-'); - /// assert one == ?"-abc"; - /// ``` - public func stripStart(self : Text, p : Pattern) : ?Text { - let s = sizeOfPattern(p); - if (s == 0) return ?self; - var cs = self.chars(); - let match = matchOfPattern(p); - switch (match(cs)) { - case (#success) return ?fromIter(cs); - case _ return null - } - }; - - /// Strips one occurrence of the given `Pattern` from the end of the input `Text`. - /// If you want to remove multiple instances of the pattern, use `Text.trimEnd()` instead. - /// - /// ```motoko include=import - /// // Try to strip a nonexistent character - /// let none = Text.stripEnd("xyz", #char '-'); - /// assert none == null; - /// // Strip just one '-' - /// let one = Text.stripEnd("xyz--", #char '-'); - /// assert one == ?"xyz-"; - /// ``` - public func stripEnd(self : Text, p : Pattern) : ?Text { - let s2 = sizeOfPattern(p); - if (s2 == 0) return ?self; - let s1 = self.size(); - if (s2 > s1) return null; - let match = matchOfPattern(p); - var cs1 = self.chars(); - var diff : Nat = s1 - s2; - while (diff > 0) { - ignore cs1.next(); - diff -= 1 - }; - switch (match(cs1)) { - case (#success) return ?extract(self, 0, s1 - s2); - case _ return null - } - }; - - /// Trims the given `Pattern` from the start of the input `Text`. - /// If you only want to remove a single instance of the pattern, use `Text.stripStart()` instead. - /// - /// ```motoko include=import - /// let trimmed = Text.trimStart("---abc", #char '-'); - /// assert trimmed == "abc"; - /// ``` - public func trimStart(self : Text, p : Pattern) : Text { - let cs = self.chars(); - let size = sizeOfPattern(p); - if (size == 0) return self; - var matchSize = 0; - let match = matchOfPattern(p); - loop { - switch (match(cs)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(cs1)) { - return if (matchSize == 0) { - self - } else { - fromIter(cs1) - } - }; - case (#fail(cs1, c)) { - return if (matchSize == 0) { - self - } else { - fromIter(cs1) # fromChar(c) # fromIter(cs) - } - } - } - } - }; - - /// Trims the given `Pattern` from the end of the input `Text`. - /// If you only want to remove a single instance of the pattern, use `Text.stripEnd()` instead. - /// - /// ```motoko include=import - /// let trimmed = Text.trimEnd("xyz---", #char '-'); - /// assert trimmed == "xyz"; - /// ``` - public func trimEnd(self : Text, p : Pattern) : Text { - let cs = CharBuffer(self.chars()); - let size = sizeOfPattern(p); - if (size == 0) return self; - let match = matchOfPattern(p); - var matchSize = 0; - label l loop { - switch (match(cs)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(cs1)) { - switch (cs1.next()) { - case null break l; - case (?_) return self - } - }; - case (#fail(cs1, c)) { - matchSize := 0; - cs.pushBack(cs1, c); - ignore cs.next() - } - } - }; - extract(self, 0, self.size() - matchSize) - }; - - /// Trims the given `Pattern` from both the start and end of the input `Text`. - /// - /// ```motoko include=import - /// let trimmed = Text.trim("---abcxyz---", #char '-'); - /// assert trimmed == "abcxyz"; - /// ``` - public func trim(self : Text, p : Pattern) : Text { - let cs = self.chars(); - let size = sizeOfPattern(p); - if (size == 0) return self; - var matchSize = 0; - let match = matchOfPattern(p); - loop { - switch (match(cs)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(cs1)) { - return if (matchSize == 0) { self } else { fromIter(cs1) } - }; - case (#fail(cs1, c)) { - let start = matchSize; - let cs2 = CharBuffer(cs); - cs2.pushBack(cs1, c); - ignore cs2.next(); - matchSize := 0; - label l loop { - switch (match(cs2)) { - case (#success) { - matchSize += size - }; // continue - case (#empty(_cs3)) { - switch (cs1.next()) { - case null break l; - case (?_) return self - } - }; - case (#fail(cs3, c1)) { - matchSize := 0; - cs2.pushBack(cs3, c1); - ignore cs2.next() - } - } - }; - return extract(self, start, self.size() - matchSize - start) - } - } - } - }; - - /// Compares `t1` and `t2` using the provided character-wise comparison function. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// assert Text.compareWith("abc", "ABC", func(c1, c2) { Char.compare(c1, c2) }) == #greater; - /// ``` - public func compareWith( - self : Text, - other : Text, - compare : (Char, Char) -> Order.Order - ) : Order.Order { - let cs1 = self.chars(); - let cs2 = other.chars(); - loop { - switch (cs1.next(), cs2.next()) { - case (null, null) { return #equal }; - case (null, ?_) { return #less }; - case (?_, null) { return #greater }; - case (?c1, ?c2) { - switch (compare(c1, c2)) { - case (#equal) {}; // continue - case other { return other } - } - } - } - } - }; - - /// Returns a UTF-8 encoded `Blob` from the given `Text`. - /// - /// ```motoko include=import - /// let blob = Text.encodeUtf8("Hello"); - /// assert blob == "\48\65\6C\6C\6F"; - /// ``` - public let encodeUtf8 : (self : Text) -> Blob = Prim.encodeUtf8; - - /// Tries to decode the given `Blob` as UTF-8. - /// Returns `null` if the blob is not valid UTF-8. - /// - /// ```motoko include=import - /// let text = Text.decodeUtf8("\48\65\6C\6C\6F"); - /// assert text == ?"Hello"; - /// ``` - public let decodeUtf8 : (self : Blob) -> ?Text = Prim.decodeUtf8; - - /// Returns the text argument in lowercase. - /// WARNING: Unicode compliant only when compiled, not interpreted. - /// - /// ```motoko include=import - /// let text = Text.toLower("Good Day"); - /// assert text == "good day"; - /// ``` - public let toLower : (self : Text) -> Text = Prim.textLowercase; - - /// Returns the text argument in uppercase. Unicode compliant. - /// WARNING: Unicode compliant only when compiled, not interpreted. - /// - /// ```motoko include=import - /// let text = Text.toUpper("Good Day"); - /// assert text == "GOOD DAY"; - /// ``` - public let toUpper : (self : Text) -> Text = Prim.textUppercase; - - /// Returns the given text value unchanged. - /// This function is provided for consistency with other modules. - /// - /// ```motoko include=import - /// assert Text.toText("Hello") == "Hello"; - /// ``` - public func toText(self : Text) : Text = self - -} diff --git a/.mops/core@2.5.0/src/Time.mo b/.mops/core@2.5.0/src/Time.mo deleted file mode 100644 index 00197a7..0000000 --- a/.mops/core@2.5.0/src/Time.mo +++ /dev/null @@ -1,62 +0,0 @@ -/// System time utilities and timers. -/// -/// The following example illustrates using the system time: -/// -/// ```motoko -/// import Int = "mo:core/Int"; -/// import Time = "mo:core/Time"; -/// -/// persistent actor { -/// var lastTime = Time.now(); -/// -/// public func greet(name : Text) : async Text { -/// let now = Time.now(); -/// let elapsedSeconds = (now - lastTime) / 1000_000_000; -/// lastTime := now; -/// return "Hello, " # name # "!" # -/// " I was last called " # Int.toText(elapsedSeconds) # " seconds ago"; -/// }; -/// }; -/// ``` -/// -/// Note: If `moc` is invoked with `-no-timer`, the importing will fail. -/// Note: The resolution of the timers is in the order of the block rate, -/// so durations should be chosen well above that. For frequent -/// canister wake-ups the heartbeat mechanism should be considered. - -import Types "Types"; -import Nat "Nat"; -import Prim "mo:⛔"; - -module { - - /// System time is represent as nanoseconds since 1970-01-01. - public type Time = Types.Time; - - /// Quantity of time expressed in `#days`, `#hours`, `#minutes`, `#seconds`, `#milliseconds`, or `#nanoseconds`. - public type Duration = Types.Duration; - - /// Current system time given as nanoseconds since 1970-01-01. The system guarantees that: - /// - /// * the time, as observed by the canister smart contract, is monotonically increasing, even across canister upgrades. - /// * within an invocation of one entry point, the time is constant. - /// - /// The system times of different canisters are unrelated, and calls from one canister to another may appear to travel "backwards in time" - /// - /// Note: While an implementation will likely try to keep the system time close to the real time, this is not formally guaranteed. - public func now() : Time = Prim.nat64ToNat(Prim.time()); - - public type TimerId = Nat; - - public func toNanoseconds(duration : Duration) : Nat { - switch duration { - case (#days n) n * 86_400_000_000_000; - case (#hours n) n * 3_600_000_000_000; - case (#minutes n) n * 60_000_000_000; - case (#seconds n) n * 1_000_000_000; - case (#milliseconds n) n * 1_000_000; - case (#nanoseconds n) n - } - }; - -} diff --git a/.mops/core@2.5.0/src/Timer.mo b/.mops/core@2.5.0/src/Timer.mo deleted file mode 100644 index 6f4f377..0000000 --- a/.mops/core@2.5.0/src/Timer.mo +++ /dev/null @@ -1,84 +0,0 @@ -/// Timers for one-off or periodic tasks. Applicable as part of the default mechanism. -/// If `moc` is invoked with `-no-timer`, the importing will fail. Furthermore, if passed `--trap-on-call-error`, a congested canister send queue may prevent timer expirations to execute at runtime. It may also deactivate the global timer. -/// -/// ```motoko name=import -/// import Timer "mo:core/Timer"; -/// ``` -/// -/// The resolution of the timers is similar to the block rate, -/// so durations should be chosen well above that. For frequent -/// canister wake-ups, consider using the [heartbeat](https://internetcomputer.org/docs/motoko/icp-features/system-functions#heartbeat) mechanism; however, when possible, canisters should prefer timers. -/// -/// The functionality described below is enabled only when the actor does not override it by declaring an explicit `system func timer`. -/// -/// Timers are _not_ persisted across upgrades. One possible strategy -/// to re-establish timers after an upgrade is to use stable variables -/// in the `post_upgrade` hook and distill necessary timer information -/// from there. -/// -/// Using timers for security (e.g., access control) is strongly discouraged. -/// Make sure to inform yourself about state-of-the-art dapp security. -/// If you must use timers for security controls, be sure -/// to consider reentrancy issues as well as the vanishing of timers on upgrades -/// and reinstalls. -/// -/// For further usage information for timers on the IC, please consult -/// [the documentation](https://internetcomputer.org/docs/building-apps/network-features/periodic-tasks-timers#timers-library-limitations). -import { setTimer = setTimerNano; cancelTimer = cancel } = "mo:⛔"; -import Nat64 = "Nat64"; -import Time "Time"; - -module { - - public type TimerId = Nat; - - /// Installs a one-off timer that upon expiration after given duration `d` - /// executes the future `job()`. - /// - /// ```motoko include=import no-repl - /// import Int "mo:core/Int"; - /// - /// func runIn30Minutes() : async () { - /// // ... - /// }; - /// let timerId = Timer.setTimer(#minutes 30, runIn30Minutes); - /// ``` - public func setTimer(duration : Time.Duration, job : () -> async ()) : TimerId { - setTimerNano(Nat64.fromNat(Time.toNanoseconds duration), false, job) - }; - - /// Installs a recurring timer that upon expiration after given duration `d` - /// executes the future `job()` and reinserts itself for another expiration. - /// - /// Note: A duration of 0 will only expire once. - /// - /// ```motoko include=import no-repl - /// func runEvery30Minutes() : async () { - /// // ... - /// }; - /// let timerId = Timer.recurringTimer(#minutes 30, runEvery30Minutes); - /// ``` - public func recurringTimer(duration : Time.Duration, job : () -> async ()) : TimerId { - setTimerNano(Nat64.fromNat(Time.toNanoseconds duration), true, job) - }; - - /// Cancels a still active timer with `(id : TimerId)`. For expired timers - /// and not recognised `id`s nothing happens. - /// - /// ```motoko include=import no-repl - /// var counter = 0; - /// var timerId : ?Timer.TimerId = null; - /// func runFiveTimes() : async () { - /// counter += 1; - /// if (counter == 5) { - /// switch (timerId) { - /// case (?id) { Timer.cancelTimer(id) }; - /// case null { assert false /* timer already cancelled */ }; - /// }; - /// } - /// }; - /// timerId := ?Timer.recurringTimer(#minutes 30, runFiveTimes); - /// ``` - public let cancelTimer : TimerId -> () = cancel; - -} diff --git a/.mops/core@2.5.0/src/Tuples.mo b/.mops/core@2.5.0/src/Tuples.mo deleted file mode 100644 index 89c5d6c..0000000 --- a/.mops/core@2.5.0/src/Tuples.mo +++ /dev/null @@ -1,365 +0,0 @@ -/// Contains modules for working with tuples of different sizes. -/// -/// Usage example: -/// -/// ```motoko -/// import { Tuple2; Tuple3 } "mo:core/Tuples"; -/// import Bool "mo:core/Bool"; -/// import Nat "mo:core/Nat"; -/// -/// let swapped = Tuple2.swap((1, "hello")); -/// assert swapped == ("hello", 1); -/// let text = Tuple3.toText((1, true, 3), Nat.toText, Bool.toText, Nat.toText); -/// assert text == "(1, true, 3)"; -/// ``` - -import Types "Types"; - -module { - - public module Tuple2 { - /// Swaps the elements of a tuple. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// - /// assert Tuple2.swap((1, "hello")) == ("hello", 1); - /// ``` - public func swap((a, b) : (A, B)) : (B, A) = (b, a); - - /// Creates a textual representation of a tuple for debugging purposes. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// assert Tuple2.toText((1, "hello"), Nat.toText, func (x: Text): Text = x) == "(1, hello)"; - /// ``` - public func toText( - self : (A, B), - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)) - ) : Text = "(" # toTextA(self.0) # ", " # toTextB(self.1) # ")"; - - /// Compares two tuples for equality. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple2.equal((1, "hello"), (1, "hello"), Nat.equal, Text.equal); - /// ``` - public func equal( - self : (A, B), - other : (A, B), - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)) - ) : Bool = equalA(self.0, other.0) and equalB(self.1, other.1); - - /// Compares two tuples lexicographically. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple2.compare((1, "hello"), (1, "world"), Nat.compare, Text.compare) == #less; - /// assert Tuple2.compare((1, "hello"), (2, "hello"), Nat.compare, Text.compare) == #less; - /// assert Tuple2.compare((1, "hello"), (1, "hello"), Nat.compare, Text.compare) == #equal; - /// assert Tuple2.compare((2, "hello"), (1, "hello"), Nat.compare, Text.compare) == #greater; - /// assert Tuple2.compare((1, "world"), (1, "hello"), Nat.compare, Text.compare) == #greater; - /// ``` - public func compare( - self : (A, B), - other : (A, B), - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)) - ) : Types.Order = switch (compareA(self.0, other.0)) { - case (#equal) compareB(self.1, other.1); - case order order - }; - - /// Creates a `toText` function for a tuple given `toText` functions for its elements. - /// This is useful when you need to reuse the same toText conversion multiple times. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// import Nat "mo:core/Nat"; - /// - /// let tupleToText = Tuple2.makeToText(Nat.toText, func x = x); - /// assert tupleToText((1, "hello")) == "(1, hello)"; - /// ``` - public func makeToText( - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)) - ) : ((A, B)) -> Text = func t = toText(t, toTextA, toTextB); - - /// Creates an `equal` function for a tuple given `equal` functions for its elements. - /// This is useful when you need to reuse the same equality comparison multiple times. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let tupleEqual = Tuple2.makeEqual(Nat.equal, Text.equal); - /// assert tupleEqual((1, "hello"), (1, "hello")); - /// ``` - public func makeEqual( - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)) - ) : ((A, B), (A, B)) -> Bool = func(t1, t2) = equal(t1, t2, equalA, equalB); - - /// Creates a `compare` function for a tuple given `compare` functions for its elements. - /// This is useful when you need to reuse the same comparison multiple times. - /// - /// ```motoko - /// import { Tuple2 } "mo:core/Tuples"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let tupleCompare = Tuple2.makeCompare(Nat.compare, Text.compare); - /// assert tupleCompare((1, "hello"), (1, "world")) == #less; - /// ``` - public func makeCompare( - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)) - ) : ((A, B), (A, B)) -> Types.Order = func(t1, t2) = compare(t1, t2, compareA, compareB) - }; - - public module Tuple3 { - /// Creates a textual representation of a 3-tuple for debugging purposes. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// assert Tuple3.toText((1, "hello", 2), Nat.toText, func (x: Text): Text = x, Nat.toText) == "(1, hello, 2)"; - /// ``` - public func toText( - self : (A, B, C), - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)), - toTextC : (implicit : (toText : C -> Text)) - ) : Text = "(" # toTextA(self.0) # ", " # toTextB(self.1) # ", " # toTextC(self.2) # ")"; - - /// Compares two 3-tuples for equality. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple3.equal((1, "hello", 2), (1, "hello", 2), Nat.equal, Text.equal, Nat.equal); - /// ``` - public func equal( - self : (A, B, C), - other : (A, B, C), - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)), - equalC : (implicit : (equal : (C, C) -> Bool)) - ) : Bool = equalA(self.0, other.0) and equalB(self.1, other.1) and equalC(self.2, other.2); - - /// Compares two 3-tuples lexicographically. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple3.compare((1, "hello", 2), (1, "world", 1), Nat.compare, Text.compare, Nat.compare) == #less; - /// assert Tuple3.compare((1, "hello", 2), (2, "hello", 2), Nat.compare, Text.compare, Nat.compare) == #less; - /// assert Tuple3.compare((1, "hello", 2), (1, "hello", 2), Nat.compare, Text.compare, Nat.compare) == #equal; - /// assert Tuple3.compare((2, "hello", 2), (1, "hello", 2), Nat.compare, Text.compare, Nat.compare) == #greater; - /// ``` - public func compare( - self : (A, B, C), - other : (A, B, C), - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)), - compareC : (implicit : (compare : (C, C) -> Types.Order)) - ) : Types.Order = switch (compareA(self.0, other.0)) { - case (#equal) { - switch (compareB(self.1, other.1)) { - case (#equal) compareC(self.2, other.2); - case order order - } - }; - case order order - }; - - /// Creates a `toText` function for a 3-tuple given `toText` functions for its elements. - /// This is useful when you need to reuse the same toText conversion multiple times. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// let toText = Tuple3.makeToText(Nat.toText, func x = x, Nat.toText); - /// assert toText((1, "hello", 2)) == "(1, hello, 2)"; - /// ``` - public func makeToText( - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)), - toTextC : (implicit : (toText : C -> Text)) - ) : ((A, B, C)) -> Text = func t = toText(t, toTextA, toTextB, toTextC); - - /// Creates an `equal` function for a 3-tuple given `equal` functions for its elements. - /// This is useful when you need to reuse the same equality comparison multiple times. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// let equal = Tuple3.makeEqual(Nat.equal, Text.equal, Nat.equal); - /// assert equal((1, "hello", 2), (1, "hello", 2)); - /// ``` - public func makeEqual( - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)), - equalC : (implicit : (equal : (C, C) -> Bool)) - ) : ((A, B, C), (A, B, C)) -> Bool = func(t1, t2) = equal(t1, t2, equalA, equalB, equalC); - - /// Creates a `compare` function for a 3-tuple given `compare` functions for its elements. - /// This is useful when you need to reuse the same comparison multiple times. - /// - /// ```motoko - /// import { Tuple3 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// let compare = Tuple3.makeCompare(Nat.compare, Text.compare, Nat.compare); - /// assert compare((1, "hello", 2), (1, "world", 1)) == #less; - /// ``` - public func makeCompare( - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)), - compareC : (implicit : (compare : (C, C) -> Types.Order)) - ) : ((A, B, C), (A, B, C)) -> Types.Order = func(t1, t2) = compare(t1, t2, compareA, compareB, compareC) - }; - - public module Tuple4 { - /// Creates a textual representation of a 4-tuple for debugging purposes. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// assert Tuple4.toText((1, "hello", 2, 3), Nat.toText, func (x: Text): Text = x, Nat.toText, Nat.toText) == "(1, hello, 2, 3)"; - /// ``` - public func toText( - self : (A, B, C, D), - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)), - toTextC : (implicit : (toText : C -> Text)), - toTextD : (implicit : (toText : D -> Text)) - ) : Text = "(" # toTextA(self.0) # ", " # toTextB(self.1) # ", " # toTextC(self.2) # ", " # toTextD(self.3) # ")"; - - /// Compares two 4-tuples for equality. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple4.equal((1, "hello", 2, 3), (1, "hello", 2, 3), Nat.equal, Text.equal, Nat.equal, Nat.equal); - /// ``` - public func equal( - self : (A, B, C, D), - other : (A, B, C, D), - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)), - equalC : (implicit : (equal : (C, C) -> Bool)), - equalD : (implicit : (equal : (D, D) -> Bool)) - ) : Bool = equalA(self.0, other.0) and equalB(self.1, other.1) and equalC(self.2, other.2) and equalD(self.3, other.3); - - /// Compares two 4-tuples lexicographically. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// assert Tuple4.compare((1, "hello", 2, 3), (1, "world", 1, 3), Nat.compare, Text.compare, Nat.compare, Nat.compare) == #less; - /// assert Tuple4.compare((1, "hello", 2, 3), (2, "hello", 2, 3), Nat.compare, Text.compare, Nat.compare, Nat.compare) == #less; - /// assert Tuple4.compare((1, "hello", 2, 3), (1, "hello", 2, 3), Nat.compare, Text.compare, Nat.compare, Nat.compare) == #equal; - /// assert Tuple4.compare((2, "hello", 2, 3), (1, "hello", 2, 3), Nat.compare, Text.compare, Nat.compare, Nat.compare) == #greater; - /// ``` - public func compare( - self : (A, B, C, D), - other : (A, B, C, D), - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)), - compareC : (implicit : (compare : (C, C) -> Types.Order)), - compareD : (implicit : (compare : (D, D) -> Types.Order)) - ) : Types.Order = switch (compareA(self.0, other.0)) { - case (#equal) { - switch (compareB(self.1, other.1)) { - case (#equal) { - switch (compareC(self.2, other.2)) { - case (#equal) compareD(self.3, other.3); - case order order - } - }; - case order order - } - }; - case order order - }; - - /// Creates a `toText` function for a 4-tuple given `toText` functions for its elements. - /// This is useful when you need to reuse the same toText conversion multiple times. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// let toText = Tuple4.makeToText(Nat.toText, func (x: Text): Text = x, Nat.toText, Nat.toText); - /// assert toText((1, "hello", 2, 3)) == "(1, hello, 2, 3)"; - /// ``` - public func makeToText( - toTextA : (implicit : (toText : A -> Text)), - toTextB : (implicit : (toText : B -> Text)), - toTextC : (implicit : (toText : C -> Text)), - toTextD : (implicit : (toText : D -> Text)) - ) : ((A, B, C, D)) -> Text = func t = toText(t, toTextA, toTextB, toTextC, toTextD); - - /// Creates an `equal` function for a 4-tuple given `equal` functions for its elements. - /// This is useful when you need to reuse the same equality comparison multiple times. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// let equal = Tuple4.makeEqual(Nat.equal, Text.equal, Nat.equal, Nat.equal); - /// assert equal((1, "hello", 2, 3), (1, "hello", 2, 3)); - /// ``` - public func makeEqual( - equalA : (implicit : (equal : (A, A) -> Bool)), - equalB : (implicit : (equal : (B, B) -> Bool)), - equalC : (implicit : (equal : (C, C) -> Bool)), - equalD : (implicit : (equal : (D, D) -> Bool)) - ) : ((A, B, C, D), (A, B, C, D)) -> Bool = func(t1, t2) = equal(t1, t2, equalA, equalB, equalC, equalD); - - /// Creates a `compare` function for a 4-tuple given `compare` functions for its elements. - /// This is useful when you need to reuse the same comparison multiple times. - /// - /// ```motoko - /// import { Tuple4 } "mo:core/Tuples"; - /// - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// let compare = Tuple4.makeCompare(Nat.compare, Text.compare, Nat.compare, Nat.compare); - /// assert compare((1, "hello", 2, 3), (1, "world", 1, 3)) == #less; - /// ``` - public func makeCompare( - compareA : (implicit : (compare : (A, A) -> Types.Order)), - compareB : (implicit : (compare : (B, B) -> Types.Order)), - compareC : (implicit : (compare : (C, C) -> Types.Order)), - compareD : (implicit : (compare : (D, D) -> Types.Order)) - ) : ((A, B, C, D), (A, B, C, D)) -> Types.Order = func(t1, t2) = compare(t1, t2, compareA, compareB, compareC, compareD) - } -} diff --git a/.mops/core@2.5.0/src/Types.mo b/.mops/core@2.5.0/src/Types.mo deleted file mode 100644 index 195972f..0000000 --- a/.mops/core@2.5.0/src/Types.mo +++ /dev/null @@ -1,181 +0,0 @@ -/// Common types used throughout the core package. -/// -/// Example usage: -/// -/// ```motoko name=import -/// import { type Result; type Iter } "mo:core/Types"; -/// -/// // Result for error handling -/// let result : Result = #ok(42); -/// -/// // Iterator for sequences -/// let iter : Iter = { next = func() { ?1 } }; -/// ``` - -import Prim "mo:⛔"; - -module { - public type Blob = Prim.Types.Blob; - public type Bool = Prim.Types.Bool; - public type Char = Prim.Types.Char; - public type Error = Prim.Types.Error; - public type ErrorCode = Prim.ErrorCode; - public type Float = Prim.Types.Float; - public type Int = Prim.Types.Int; - public type Int8 = Prim.Types.Int8; - public type Int16 = Prim.Types.Int16; - public type Int32 = Prim.Types.Int32; - public type Int64 = Prim.Types.Int64; - public type Nat = Prim.Types.Nat; - public type Nat8 = Prim.Types.Nat8; - public type Nat16 = Prim.Types.Nat16; - public type Nat32 = Prim.Types.Nat32; - public type Nat64 = Prim.Types.Nat64; - public type Principal = Prim.Types.Principal; - public type Region = Prim.Types.Region; - public type Text = Prim.Types.Text; - - public type Hash = Nat32; - public type Iter = { next : () -> ?T }; - public type Order = { #less; #equal; #greater }; - public type Result = { #ok : T; #err : E }; - public type Pattern = { - #char : Char; - #text : Text; - #predicate : (Char -> Bool) - }; - public type Time = Int; - public type Duration = { - #days : Nat; - #hours : Nat; - #minutes : Nat; - #seconds : Nat; - #milliseconds : Nat; - #nanoseconds : Nat - }; - public type TimerId = Nat; - - public type List = { - var blocks : [var [var ?T]]; - var blockIndex : Nat; - var elementIndex : Nat - }; - - public module Queue { - public type Queue = { - var front : ?Node; - var back : ?Node; - var size : Nat - }; - - public type Node = { - value : T; - var next : ?Node; - var previous : ?Node - } - }; - public type Queue = Queue.Queue; - - public module PriorityQueue { - public type PriorityQueue = { - heap : List - } - }; - public type PriorityQueue = PriorityQueue.PriorityQueue; - - public module Set { - public type Node = { - #leaf : Leaf; - #internal : Internal - }; - - public type Data = { - elements : [var ?T]; - var count : Nat - }; - - public type Internal = { - data : Data; - children : [var ?Node] - }; - - public type Leaf = { - data : Data - }; - - public type Set = { - var root : Node; - var size : Nat - } - }; - public type Set = Set.Set; - - public module Map { - public type Node = { - #leaf : Leaf; - #internal : Internal - }; - - public type Data = { - kvs : [var ?(K, V)]; - var count : Nat - }; - - public type Internal = { - data : Data; - children : [var ?Node] - }; - - public type Leaf = { - data : Data - }; - - public type Map = { - var root : Node; - var size : Nat - } - }; - - public type Map = Map.Map; - - public module Stack { - public type Stack = { - var top : Pure.List; - var size : Nat - } - }; - public type Stack = Stack.Stack; - - public module Pure { - public type List = ?(T, List); - - public module Map { - public type Map = { - size : Nat; - root : Tree - }; - public type Tree = { - #red : (Tree, K, V, Tree); - #black : (Tree, K, V, Tree); - #leaf - }; - - }; - public type Map = Map.Map; - - public type Queue = (List, Nat, List); - - public module Set { - public type Tree = { - #red : (Tree, T, Tree); - #black : (Tree, T, Tree); - #leaf - }; - - public type Set = { size : Nat; root : Tree } - }; - - public type Set = Set.Set; - - } -} diff --git a/.mops/core@2.5.0/src/VarArray.mo b/.mops/core@2.5.0/src/VarArray.mo deleted file mode 100644 index 7dac8c0..0000000 --- a/.mops/core@2.5.0/src/VarArray.mo +++ /dev/null @@ -1,1407 +0,0 @@ -/// Provides extended utility functions on mutable Arrays (`[var]`). -/// -/// Note the difference between mutable (`[var]`) and immutable (`[]`) arrays. -/// Mutable arrays allow their elements to be modified after creation, while -/// immutable arrays are fixed once created. -/// -/// WARNING: If you are looking for a list that can grow and shrink in size, -/// it is recommended you use `List` for those purposes. -/// Arrays must be created with a fixed size. -/// -/// Import from the core package to use this module. -/// ```motoko name=import -/// import VarArray "mo:core/VarArray"; -/// ``` - -import Types "Types"; -import Order "Order"; -import Result "Result"; -import Option "Option"; -import Prim "mo:⛔"; -import InsertionSort "internal/SortHelper"; - -module { - let nat = Prim.nat32ToNat; - - /// Creates an empty mutable array (equivalent to `[var]`). - /// - /// ```motoko include=import - /// let array = VarArray.empty(); - /// assert array.size() == 0; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func empty() : [var T] = [var]; - - /// Creates a mutable array containing `item` repeated `size` times. - /// - /// ```motoko include=import - /// import Text "mo:core/Text"; - /// - /// let array = VarArray.repeat("Echo", 3); - /// assert VarArray.equal(array, [var "Echo", "Echo", "Echo"], Text.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func repeat(item : T, size : Nat) : [var T] = Prim.Array_init(size, item); - - /// Duplicates `array`, returning a shallow copy of the original. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array1 = [var 1, 2, 3]; - /// let array2 = VarArray.clone(array1); - /// array2[0] := 0; - /// assert VarArray.equal(array1, [var 1, 2, 3], Nat.equal); - /// assert VarArray.equal(array2, [var 0, 2, 3], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func clone(self : [var T]) : [var T] = Prim.Array_tabulateVar(self.size(), func i = self[i]); - - /// Creates a mutable array of size `size`. Each element at index i - /// is created by applying `generator` to i. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array : [var Nat] = VarArray.tabulate(4, func i = i * 2); - /// assert VarArray.equal(array, [var 0, 2, 4, 6], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `generator` runs in O(1) time and space. - public let tabulate : (size : Nat, generator : Nat -> T) -> [var T] = Prim.Array_tabulateVar; - - /// Tests if two arrays contain equal values (i.e. they represent the same - /// list of elements). Uses `equal` to compare elements in the arrays. - /// - /// ```motoko include=import - /// // Use the equal function from the Nat module to compare Nats - /// import Nat "mo:core/Nat"; - /// - /// let array1 = [var 0, 1, 2, 3]; - /// let array2 = [var 0, 1, 2, 3]; - /// assert VarArray.equal(array1, array2, Nat.equal); - /// ``` - /// - /// Runtime: O(size1 + size2) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func equal(self : [var T], other : [var T], equal : (implicit : (T, T) -> Bool)) : Bool { - let size1 = self.size(); - let size2 = other.size(); - if (size1 != size2) { - return false - }; - var i = 0; - while (i < size1) { - if (not equal(self[i], other[i])) { - return false - }; - i += 1 - }; - true - }; - - /// Returns the first value in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let array = [var 1, 9, 4, 8]; - /// let found = VarArray.find(array, func x = x > 8); - /// assert found == ?9; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func find(self : [var T], predicate : T -> Bool) : ?T { - for (element in self.vals()) { - if (predicate element) { - return ?element - } - }; - null - }; - - /// Returns the first index in `array` for which `predicate` returns true. - /// If no element satisfies the predicate, returns null. - /// - /// ```motoko include=import - /// let array = [var 'A', 'B', 'C', 'D']; - /// let found = VarArray.findIndex(array, func(x) { x == 'C' }); - /// assert found == ?2; - /// ``` - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func findIndex(self : [var T], predicate : T -> Bool) : ?Nat { - for ((index, element) in enumerate(self)) { - if (predicate element) { - return ?index - } - }; - null - }; - - /// Create a new mutable array by concatenating the values of `array1` and `array2`. - /// Note that `VarArray.concat` copies its arguments and has linear complexity. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array1 = [var 1, 2, 3]; - /// let array2 = [var 4, 5, 6]; - /// let result = VarArray.concat(array1, array2); - /// assert VarArray.equal(result, [var 1, 2, 3, 4, 5, 6], Nat.equal); - /// ``` - /// Runtime: O(size1 + size2) - /// - /// Space: O(size1 + size2) - public func concat(self : [var T], other : [var T]) : [var T] { - let size1 = self.size(); - let size2 = other.size(); - tabulate( - size1 + size2, - func i { - if (i < size1) { - self[i] - } else { - other[i - size1] - } - } - ) - }; - - /// Creates a new sorted copy of the mutable array according to `compare`. - /// Sort is deterministic and stable. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 4, 2, 6]; - /// let sorted = VarArray.sort(array, Nat.compare); - /// assert VarArray.equal(sorted, [var 2, 4, 6], Nat.equal); - /// ``` - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sort(self : [var T], compare : (implicit : (T, T) -> Order.Order)) : [var T] { - let newArray = clone(self); - sortInPlace(newArray, compare); - newArray - }; - - /// Sorts the elements in a mutable array in place according to `compare`. - /// Sort is deterministic and stable. This modifies the original array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 4, 2, 6]; - /// VarArray.sortInPlace(array, Nat.compare); - /// assert VarArray.equal(array, [var 2, 4, 6], Nat.equal); - /// ``` - /// Runtime: O(size * log(size)) - /// - /// Space: O(size) - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func sortInPlace(self : [var T], compare : (implicit : (T, T) -> Order.Order)) : () { - let size = Prim.natToNat32(self.size()); - if (size <= 1) return; - if (size <= 8) { - InsertionSort.insertionSortSmall(self, self, compare, 0 : Nat32, size); - return - }; - let buffer = repeat(self[0], nat(size / 2)); - mergeSortRec(self, buffer, compare, 0 : Nat32, size, true, 0 : Nat32) - }; - - // input data is alwways in array - // even: write output data to array in place - // odd: write output data to buffer at offset - // offset is only used when odd - func mergeSortRec( - array : [var T], - buffer : [var T], - compare : (T, T) -> Order.Order, - from : Nat32, - to : Nat32, - even : Bool, - offset : Nat32 - ) { - debug assert from < to; - let size = to -% from; - debug assert size >= 4; - - if (size <= 8) { - if (even) { - InsertionSort.insertionSortSmall(array, array, compare, from, size); // sorts array in place - } else { - InsertionSort.insertionSortSmallMove(array, buffer, compare, from, size, offset); // sorts to buffer at offset - }; - return - }; - - let len1 = size / 2; - let mid = from +% len1; - if (even) { - // merge to array in place - mergeSortRec(array, buffer, compare, mid, to, true, 0 : Nat32); // sort upper half to array in place - mergeSortRec(array, buffer, compare, from, mid, false, 0 : Nat32); // sort lower half to beginning of buffer - merge1(array, buffer, compare, from, mid, to); // merge to array in place - } else { - // merge to buffer at offset - mergeSortRec(array, buffer, compare, from, mid, true, 0 : Nat32); // lower half to array in place - mergeSortRec(array, buffer, compare, mid, to, false, offset +% len1); // sort upper half to buffer starting shifted offset - merge2(array, buffer, compare, from, mid, size, offset); // merge to buffer at offset - } - }; - - func merge1(array : [var T], buffer : [var T], compare : (T, T) -> Order.Order, from : Nat32, mid : Nat32, to : Nat32) { - debug assert from < mid; - debug assert mid < to; - let len = mid -% from; - var pos = from; - var i = 0 : Nat32; - var j = mid; - - var iElem = buffer[nat(i)]; - var jElem = array[nat(j)]; - label L loop { - switch (compare(jElem, iElem)) { - case (#less) { - array[nat(pos)] := jElem; - j +%= 1; - pos +%= 1; - if (j == to) { - while (i < len) { - array[nat(pos)] := buffer[nat(i)]; - i +%= 1; - pos +%= 1 - }; - break L - }; - jElem := array[nat(j)] - }; - case (_) { - array[nat(pos)] := iElem; - i +%= 1; - pos +%= 1; - if (i == len) break L; - iElem := buffer[nat(i)] - } - } - } - }; - - func merge2(array : [var T], buffer : [var T], compare : (T, T) -> Order.Order, from : Nat32, mid : Nat32, size : Nat32, offset : Nat32) { - debug assert from < mid; - debug assert mid < from +% size; - let len = mid -% from; - var pos = offset; - var i = from; - var j = offset +% len; - let j_max = offset +% size; - - var iElem = array[nat(i)]; - var jElem = buffer[nat(j)]; - label L loop { - switch (compare(jElem, iElem)) { - case (#less) { - buffer[nat(pos)] := jElem; - j +%= 1; - pos +%= 1; - if (j == j_max) { - while (i < mid) { - buffer[nat(pos)] := array[nat(i)]; - i +%= 1; - pos +%= 1 - }; - break L - }; - jElem := buffer[nat(j)] - }; - case (_) { - buffer[nat(pos)] := iElem; - i +%= 1; - pos +%= 1; - if (i == mid) break L; - iElem := array[nat(i)] - } - } - } - }; - - /// Creates a new mutable array by reversing the order of elements in `array`. - /// The original array is not modified. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 10, 11, 12]; - /// let reversed = VarArray.reverse(array); - /// assert VarArray.equal(reversed, [var 12, 11, 10], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func reverse(self : [var T]) : [var T] { - let size = self.size(); - tabulate(size, func i = self[size - i - 1]) - }; - - /// Reverses the order of elements in a mutable array in place. - /// This modifies the original array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 10, 11, 12]; - /// VarArray.reverseInPlace(array); - /// assert VarArray.equal(array, [var 12, 11, 10], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func reverseInPlace(self : [var T]) : () { - let size = self.size(); - if (size == 0) { - return - }; - var i = 0; - var j = (size - 1) : Nat; - while (i < j) { - let temp = self[i]; - self[i] := self[j]; - self[j] := temp; - i += 1; - j -= 1 - } - }; - - /// Calls `f` with each element in `array`. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// var sum = 0; - /// let array = [var 0, 1, 2, 3]; - /// VarArray.forEach(array, func(x) { - /// sum += x; - /// }); - /// assert sum == 6; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEach(self : [var T], f : T -> ()) { - for (item in self.vals()) { - f(item) - } - }; - - /// Creates a new mutable array by applying `f` to each element in `array`. `f` "maps" - /// each element it is applied to of type `T` to an element of type `R`. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 0, 1, 2, 3]; - /// let array2 = VarArray.map(array, func x = x * 2); - /// assert VarArray.equal(array2, [var 0, 2, 4, 6], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func map(self : [var T], f : T -> R) : [var R] { - tabulate( - self.size(), - func(index) { - f(self[index]) - } - ) - }; - - /// Applies `f` to each element of `array` in place, - /// retaining the original ordering of elements. - /// This modifies the original array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 0, 1, 2, 3]; - /// VarArray.mapInPlace(array, func x = x * 3); - /// assert VarArray.equal(array, [var 0, 3, 6, 9], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapInPlace(self : [var T], f : T -> T) { - var index = 0; - let size = self.size(); - while (index < size) { - self[index] := f(self[index]); - index += 1 - } - }; - - /// Creates a new mutable array by applying `predicate` to every element - /// in `array`, retaining the elements for which `predicate` returns true. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 4, 2, 6, 1, 5]; - /// let evenElements = VarArray.filter(array, func x = x % 2 == 0); - /// assert VarArray.equal(evenElements, [var 4, 2, 6], Nat.equal); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func filter(self : [var T], f : T -> Bool) : [var T] { - var count = 0; - let keep = Prim.Array_tabulate( - self.size(), - func i { - if (f(self[i])) { - count += 1; - true - } else { - false - } - } - ); - var nextKeep = 0; - tabulate( - count, - func _ { - while (not keep[nextKeep]) { - nextKeep += 1 - }; - nextKeep += 1; - self[nextKeep - 1] - } - ) - }; - - /// Creates a new mutable array by applying `f` to each element in `array`, - /// and keeping all non-null elements. The ordering is retained. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// let array = [var 4, 2, 0, 1]; - /// let newArray = - /// VarArray.filterMap( // mapping from Nat to Text values - /// array, - /// func x = if (x == 0) { null } else { ?Nat.toText(100 / x) } // can't divide by 0, so return null - /// ); - /// assert VarArray.equal(newArray, [var "25", "50", "100"], Text.equal); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func filterMap(self : [var T], f : T -> ?R) : [var R] { - var count = 0; - let options = Prim.Array_tabulate( - self.size(), - func i { - let result = f(self[i]); - switch (result) { - case (?element) { - count += 1; - result - }; - case null { - null - } - } - } - ); - - var nextSome = 0; - tabulate( - count, - func _ { - while (Option.isNull(options[nextSome])) { - nextSome += 1 - }; - nextSome += 1; - switch (options[nextSome - 1]) { - case (?element) element; - case null { - Prim.trap "VarArray.filterMap(): malformed array" - } - } - } - ) - }; - - /// Creates a new mutable array by applying `f` to each element in `array`. - /// If any invocation of `f` produces an `#err`, returns an `#err`. Otherwise - /// returns an `#ok` containing the new array. - /// - /// ```motoko include=import - /// import Result "mo:core/Result"; - /// - /// let array = [var 4, 3, 2, 1, 0]; - /// // divide 100 by every element in the array - /// let result = VarArray.mapResult(array, func x { - /// if (x > 0) { - /// #ok(100 / x) - /// } else { - /// #err "Cannot divide by zero" - /// } - /// }); - /// assert Result.isErr(result); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - /// @deprecated M0235 - public func mapResult(self : [var T], f : T -> Result.Result) : Result.Result<[var R], E> { - let size = self.size(); - - var error : ?Result.Result<[var R], E> = null; - let results = tabulate( - size, - func i { - switch (f(self[i])) { - case (#ok element) { - ?element - }; - case (#err e) { - switch (error) { - case null { - // only take the first error - error := ?(#err e) - }; - case _ {} - }; - null - } - } - } - ); - - switch error { - case null { - // unpack the option - #ok( - map( - results, - func element { - switch element { - case (?element) { - element - }; - case null { - Prim.trap "VarArray.mapResults(): malformed array" - } - } - } - ) - ) - }; - case (?error) { - error - } - } - }; - - /// Creates a new array by applying `f` to each element in `array` and its index. - /// Retains original ordering of elements. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 10, 10, 10, 10]; - /// let newArray = VarArray.mapEntries(array, func (x, i) = i * x); - /// assert VarArray.equal(newArray, [var 0, 10, 20, 30], Nat.equal); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapEntries(self : [var T], f : (T, Nat) -> R) : [var R] { - tabulate(self.size(), func i = f(self[i], i)) - }; - - /// Creates a new mutable array by applying `k` to each element in `array`, - /// and concatenating the resulting arrays in order. - /// - /// ```motoko include=import - /// import Int "mo:core/Int" - /// - /// let array = [var 1, 2, 3, 4]; - /// let newArray = VarArray.flatMap(array, func x = [x, -x].vals()); - /// assert VarArray.equal(newArray, [var 1, -1, 2, -2, 3, -3, 4, -4], Int.equal); - /// ``` - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `k` runs in O(1) time and space. - public func flatMap(self : [var T], k : T -> Types.Iter) : [var R] { - var flatSize = 0; - let arrays = Prim.Array_tabulate<[var R]>( - self.size(), - func i { - let subArray = fromIter(k(self[i])); // TODO: optimize - flatSize += subArray.size(); - subArray - } - ); - - // could replace with a call to flatten, - // but it would require an extra pass (to compute `flatSize`) - var outer = 0; - var inner = 0; - tabulate( - flatSize, - func _ { - while (inner == arrays[outer].size()) { - inner := 0; - outer += 1 - }; - let element = arrays[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// ```motoko include=import - /// import {add} "mo:core/Nat"; - /// - /// let array = [var 4, 2, 0, 1]; - /// let sum = - /// VarArray.foldLeft( - /// array, - /// 0, // start the sum at 0 - /// func(sumSoFar, x) = sumSoFar + x // this entire function can be replaced with `add`! - /// ); - /// assert sum == 7; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(self : [var T], base : A, combine : (A, T) -> A) : A { - var acc = base; - for (element in self.vals()) { - acc := combine(acc, element) - }; - acc - }; - - /// Collapses the elements in `array` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// ```motoko include=import - /// import {toText} "mo:core/Nat"; - /// - /// let array = [var 1, 9, 4, 8]; - /// let bookTitle = VarArray.foldRight(array, "", func(x, acc) = toText(x) # acc); - /// assert bookTitle == "1948"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldRight(self : [var T], base : A, combine : (T, A) -> A) : A { - var acc = base; - let size = self.size(); - var i = size; - while (i > 0) { - i -= 1; - acc := combine(self[i], acc) - }; - acc - }; - - /// Combines an iterator of mutable arrays into a single mutable array. - /// Retains the original ordering of the elements. - /// - /// Consider using `VarArray.flatten()` for better performance. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let arrays : [[var Nat]] = [[var 0, 1, 2], [var 2, 3], [var], [var 4]]; - /// let joinedArray = VarArray.join(arrays.vals()); - /// assert VarArray.equal(joinedArray, [var 0, 1, 2, 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(number of elements in array) - /// - /// Space: O(number of elements in array) - public func join(self : Types.Iter<[var T]>) : [var T] { - flatten(fromIter(self)) - }; - - /// Combines a mutable array of mutable arrays into a single mutable array. Retains the original - /// ordering of the elements. - /// - /// This has better performance compared to `VarArray.join()`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let arrays : [var [var Nat]] = [var [var 0, 1, 2], [var 2, 3], [var], [var 4]]; - /// let flatArray = VarArray.flatten(arrays); - /// assert VarArray.equal(flatArray, [var 0, 1, 2, 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(number of elements in array) - /// - /// Space: O(number of elements in array) - public func flatten(self : [var [var T]]) : [var T] { - var flatSize = 0; - for (subArray in self.vals()) { - flatSize += subArray.size() - }; - - var outer = 0; - var inner = 0; - tabulate( - flatSize, - func _ { - while (inner == self[outer].size()) { - inner := 0; - outer += 1 - }; - let element = self[outer][inner]; - inner += 1; - element - } - ) - }; - - /// Create an array containing a single value. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = VarArray.singleton(2); - /// assert VarArray.equal(array, [var 2], Nat.equal); - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func singleton(element : T) : [var T] = [var element]; - - /// Returns the size of a mutable array. Equivalent to `array.size()`. - public func size(self : [var T]) : Nat = self.size(); - - /// Returns whether a mutable array is empty, i.e. contains zero elements. - public func isEmpty(self : [var T]) : Bool = self.size() == 0; - - /// Transforms an immutable array into a mutable array. - /// - /// ```motoko include=import - /// let array = [0, 1, 2]; - /// let varArray = VarArray.fromArray(array); - /// assert varArray.size() == 3; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// @deprecated M0235 - public func fromArray(array : [T]) : [var T] = Prim.Array_tabulateVar(array.size(), func i = array[i]); - - /// Converts an iterator to a mutable array. - public func fromIter(iter : Types.Iter) : [var T] { - var list : Types.Pure.List = null; - var size = 0; - label l loop { - switch (iter.next()) { - case (?element) { - list := ?(element, list); - size += 1 - }; - case null { break l } - } - }; - if (size == 0) { return [var] }; - let array = Prim.Array_init( - size, - switch list { - case (?(h, _)) h; - case null { - Prim.trap("VarArray.fromIter(): unreachable") - } - } - ); - var i = size; - while (i > 0) { - i -= 1; - switch list { - case (?(h, t)) { - array[i] := h; - list := t - }; - case null { - Prim.trap("VarArray.fromIter(): unreachable") - } - } - }; - array - }; - - /// Returns an iterator (`Iter`) over the indices of `array`. - /// An iterator provides a single method `next()`, which returns - /// indices in order, or `null` when out of index to iterate over. - /// - /// NOTE: You can also use `array.keys()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// let array = [var 10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.keys()) { - /// sum += element; - /// }; - /// assert sum == 3; // 0 + 1 + 2 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func keys(self : [var T]) : Types.Iter = self.keys(); - - /// Iterator provides a single method `next()`, which returns - /// elements in order, or `null` when out of elements to iterate over. - /// - /// Note: You can also use `array.values()` instead of this function. See example - /// below. - /// - /// ```motoko include=import - /// let array = [var 10, 11, 12]; - /// - /// var sum = 0; - /// for (element in array.values()) { - /// sum += element; - /// }; - /// assert sum == 33; // 10 + 11 + 12 - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func values(self : [var T]) : Types.Iter = self.vals(); - - /// Returns an iterator that provides pairs of (index, element) in order, or `null` - /// when out of elements to iterate over. - /// - /// ```motoko include=import - /// let array = [var 10, 11, 12]; - /// - /// var sum = 0; - /// for ((index, element) in VarArray.enumerate(array)) { - /// sum += element; - /// }; - /// assert sum == 33; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func enumerate(self : [var T]) : Types.Iter<(Nat, T)> = object { - let size = self.size(); - var index = 0; - public func next() : ?(Nat, T) { - if (index >= size) { - return null - }; - let i = index; - index += 1; - ?(i, self[i]) - } - }; - - /// Returns true if all elements in `array` satisfy the predicate function. - /// - /// ```motoko include=import - /// let array = [var 1, 2, 3, 4]; - /// assert VarArray.all(array, func x = x > 0); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func all(self : [var T], predicate : T -> Bool) : Bool { - for (element in self.values()) { - if (not predicate(element)) { - return false - } - }; - true - }; - - /// Returns true if any element in `array` satisfies the predicate function. - /// - /// ```motoko include=import - /// let array = [var 1, 2, 3, 4]; - /// assert VarArray.any(array, func x = x > 3); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `predicate` runs in O(1) time and space. - public func any(self : [var T], predicate : T -> Bool) : Bool { - for (element in self.values()) { - if (predicate(element)) { - return true - } - }; - false - }; - - /// Returns the index of the first `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.indexOf(array, Char.equal, 'c') == ?0; - /// assert VarArray.indexOf(array, Char.equal, 'f') == ?2; - /// assert VarArray.indexOf(array, Char.equal, 'g') == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func indexOf(self : [var T], equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = nextIndexOf(self, equal, element, 0); - - /// Returns the index of the next occurence of `element` in the `array` starting from the `from` index (inclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.nextIndexOf(array, Char.equal, 'c', 0) == ?0; - /// assert VarArray.nextIndexOf(array, Char.equal, 'f', 0) == ?2; - /// assert VarArray.nextIndexOf(array, Char.equal, 'f', 2) == ?2; - /// assert VarArray.nextIndexOf(array, Char.equal, 'f', 3) == ?3; - /// assert VarArray.nextIndexOf(array, Char.equal, 'f', 4) == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func nextIndexOf(self : [var T], equal : (implicit : (T, T) -> Bool), element : T, fromInclusive : Nat) : ?Nat { - var index = fromInclusive; - let size = self.size(); - while (index < size) { - if (equal(self[index], element)) { - return ?index - } else { - index += 1 - } - }; - null - }; - - /// Returns the index of the last `element` in the `array`. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.lastIndexOf(array, Char.equal, 'c') == ?0; - /// assert VarArray.lastIndexOf(array, Char.equal, 'f') == ?3; - /// assert VarArray.lastIndexOf(array, Char.equal, 'e') == ?5; - /// assert VarArray.lastIndexOf(array, Char.equal, 'g') == null; - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func lastIndexOf(self : [var T], equal : (implicit : (T, T) -> Bool), element : T) : ?Nat = prevIndexOf(self, equal, element, self.size()); - - /// Returns the index of the previous occurence of `element` in the `array` starting from the `from` index (exclusive). - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.prevIndexOf(array, Char.equal, 'c', array.size()) == ?0; - /// assert VarArray.prevIndexOf(array, Char.equal, 'e', array.size()) == ?5; - /// assert VarArray.prevIndexOf(array, Char.equal, 'e', 5) == ?4; - /// assert VarArray.prevIndexOf(array, Char.equal, 'e', 4) == null; - /// ``` - /// - /// Runtime: O(array.size()); - /// Space: O(1); - public func prevIndexOf(self : [var T], equal : (implicit : (T, T) -> Bool), element : T, fromExclusive : Nat) : ?Nat { - var i = fromExclusive; - while (i > 0) { - i -= 1; - if (equal(self[i], element)) { - return ?i - } - }; - null - }; - - /// Returns true if the `array` contains `element` using the provided `equal` function. - /// - /// ```motoko include=import - /// import Char "mo:core/Char"; - /// - /// let array = [var 'c', 'o', 'f', 'f', 'e', 'e']; - /// assert VarArray.contains(array, Char.equal, 'f'); - /// assert not VarArray.contains(array, Char.equal, 'g'); - /// ``` - /// - /// Runtime: O(array.size()) - /// - /// Space: O(1) - public func contains(self : [var T], equal : (implicit : (T, T) -> Bool), element : T) : Bool { - for (item in self.vals()) { - if (equal(item, element)) { - return true - } - }; - false - }; - - /// Returns an iterator over a slice of `array` starting at `fromInclusive` up to (but not including) `toExclusive`. - /// - /// Negative indices are relative to the end of the array. For example, `-1` corresponds to the last element in the array. - /// - /// If the indices are out of bounds, they are clamped to the array bounds. - /// If the first index is greater than the second, the function returns an empty iterator. - /// - /// ```motoko include=import - /// let array = [var 1, 2, 3, 4, 5]; - /// let iter1 = VarArray.range(array, 3, array.size()); - /// assert iter1.next() == ?4; - /// assert iter1.next() == ?5; - /// assert iter1.next() == null; - /// - /// let iter2 = VarArray.range(array, 3, -1); - /// assert iter2.next() == ?4; - /// assert iter2.next() == null; - /// - /// let iter3 = VarArray.range(array, 0, 0); - /// assert iter3.next() == null; - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func range(self : [var T], fromInclusive : Int, toExclusive : Int) : Types.Iter { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (values are non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - object { - var pos = start; - public func next() : ?T { - if (pos >= end) { - null - } else { - let elem = self[pos]; - pos += 1; - ?elem - } - } - } - }; - - /// Returns a new array containing elements from `array` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// let array = [var 1, 2, 3, 4, 5]; - /// - /// let slice1 = VarArray.sliceToArray(array, 1, 4); - /// assert slice1 == [2, 3, 4]; - /// - /// let slice2 = VarArray.sliceToArray(array, 1, -1); - /// assert slice2 == [2, 3, 4]; - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToArray(self : [var T], fromInclusive : Int, toExclusive : Int) : [T] { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - if (start >= end) { - return [] - }; - Prim.Array_tabulate(end - start, func i = self[start + i]) - }; - - /// Returns a new mutable array containing elements from `array` starting at index `fromInclusive` up to (but not including) index `toExclusive`. - /// If the indices are out of bounds, they are clamped to the array bounds. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 1, 2, 3, 4, 5]; - /// - /// let slice1 = VarArray.sliceToVarArray(array, 1, 4); - /// assert VarArray.equal(slice1, [var 2, 3, 4], Nat.equal); - /// - /// let slice2 = VarArray.sliceToVarArray(array, 1, -1); - /// assert VarArray.equal(slice2, [var 2, 3, 4], Nat.equal); - /// ``` - /// - /// Runtime: O(toExclusive - fromInclusive) - /// - /// Space: O(toExclusive - fromInclusive) - public func sliceToVarArray(self : [var T], fromInclusive : Int, toExclusive : Int) : [var T] { - let size = self.size(); - // Convert negative indices to positive and handle bounds - let startInt = if (fromInclusive < 0) { - let s = size + fromInclusive; - if (s < 0) { 0 } else { s } - } else { - if (fromInclusive > size) { size } else { fromInclusive } - }; - let endInt = if (toExclusive < 0) { - let e = size + toExclusive; - if (e < 0) { 0 } else { e } - } else { - if (toExclusive > size) { size } else { toExclusive } - }; - // Convert to Nat (always non-negative due to bounds checking above) - let start = Prim.abs(startInt); - let end = Prim.abs(endInt); - if (start >= end) { - return [var] - }; - Prim.Array_tabulateVar(end - start, func i = self[start + i]) - }; - - /// Transforms a mutable array into an immutable array. - /// - /// ```motoko include=import - /// let varArray = [var 0, 1, 2]; - /// varArray[2] := 3; - /// let array = VarArray.toArray(varArray); - /// assert array == [0, 1, 3]; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func toArray(self : [var T]) : [T] = Prim.Array_tabulate(self.size(), func i = self[i]); - - /// Converts the mutable array to its textual representation using `f` to convert each element to `Text`. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 1, 2, 3]; - /// assert VarArray.toText(array, Nat.toText) == "[var 1, 2, 3]"; - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func toText(self : [var T], f : (implicit : (toText : T -> Text))) : Text { - let size = self.size(); - if (size == 0) { return "[var]" }; - var text = "[var "; - var i = 0; - while (i < size) { - if (i != 0) { - text #= ", " - }; - text #= f(self[i]); - i += 1 - }; - text #= "]"; - text - }; - - /// Compares two mutable arrays using the provided comparison function for elements. - /// Returns #less, #equal, or #greater if `array1` is less than, equal to, - /// or greater than `array2` respectively. - /// - /// If arrays have different sizes but all elements up to the shorter length are equal, - /// the shorter array is considered #less than the longer array. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// let array1 = [var 1, 2, 3]; - /// let array2 = [var 1, 2, 4]; - /// assert VarArray.compare(array1, array2, Nat.compare) == #less; - /// - /// let array3 = [var 1, 2]; - /// let array4 = [var 1, 2, 3]; - /// assert VarArray.compare(array3, array4, Nat.compare) == #less; - /// ``` - /// - /// Runtime: O(min(size1, size2)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func compare(self : [var T], other : [var T], compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - let size1 = self.size(); - let size2 = other.size(); - var i = 0; - let minSize = if (size1 < size2) { size1 } else { size2 }; - while (i < minSize) { - switch (compare(self[i], other[i])) { - case (#less) { return #less }; - case (#greater) { return #greater }; - case (#equal) { i += 1 } - } - }; - if (size1 < size2) { #less } else if (size1 > size2) { #greater } else { - #equal - } - }; - - /// Performs binary search on a sorted mutable array to find the index of the `element`. - /// Returns `#found(index)` if the element is found, or `#insertionIndex(index)` with the index - /// - /// If there are multiple equal elements, no guarantee is made about which index is returned. - /// The array must be sorted in ascending order according to the `compare` function. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let sorted = [var 1, 3, 5, 7, 9, 11]; - /// assert VarArray.binarySearch(sorted, Nat.compare, 5) == #found(2); - /// assert VarArray.binarySearch(sorted, Nat.compare, 6) == #insertionIndex(3); - /// ``` - /// - /// Runtime: O(log(size)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func binarySearch(self : [var T], compare : (implicit : (T, T) -> Order.Order), element : T) : { - #found : Nat; - #insertionIndex : Nat - } { - var left = 0; - var right = self.size(); - while (left < right) { - let mid = (left + right) / 2; - switch (compare(self[mid], element)) { - case (#less) left := mid + 1; - case (#greater) right := mid; - case (#equal) return #found mid - } - }; - #insertionIndex left - }; - - /// Checks whether the mutable `array` is sorted according to the `compare` function. - /// - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// let array = [var 1, 2, 3]; - /// assert VarArray.isSorted(array, Nat.compare); - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `compare` runs in O(1) time and space. - public func isSorted(self : [var T], compare : (implicit : (T, T) -> Order.Order)) : Bool { - let size = self.size(); - if (size <= 1) return true; - var i = 1; - while (i < size) { - switch (compare(self[i - 1], self[i])) { - case (#greater) return false; - case _ { i += 1 } - } - }; - true - } - -} diff --git a/.mops/core@2.5.0/src/WeakReference.mo b/.mops/core@2.5.0/src/WeakReference.mo deleted file mode 100644 index a7c4a69..0000000 --- a/.mops/core@2.5.0/src/WeakReference.mo +++ /dev/null @@ -1,59 +0,0 @@ -/// Module that implements a weak reference to an object. -/// -/// ATTENTION: This functionality does not work with classical persistence (`--legacy-persistence` moc flag). -/// -/// Usage example: -/// Import from the core package to use this module. -/// ```motoko name=import -/// import WeakReference "mo:core/WeakReference"; -/// ``` - -import Prim "mo:⛔" - -module { - public type WeakReference = { - ref : weak T - }; - - /// Allocate a new weak reference to the given object. - /// - /// The `obj` parameter is the object to allocate a weak reference for. - /// Returns a new weak reference pointingto the given object. - /// ```motoko include=import - /// let obj = { x = 1 }; - /// let weakRef = WeakReference.allocate(obj); - /// ``` - public func allocate(obj : T) : WeakReference { - return { ref = Prim.allocWeakRef(obj) } - }; - - /// Get the value that the weak reference is pointing to. - /// - /// The `self` parameter is the weak reference pointing to the value the function returns. - /// The function returns the value that the weak reference is pointing to, - /// or `null` if the value has been collected by the garbage collector. - /// ```motoko include=import - /// let obj = { x = 1 }; - /// let weakRef = WeakReference.allocate(obj); - /// let value = weakRef.get(); - /// ``` - public func get(self : WeakReference) : ?T { - return Prim.weakGet(self.ref) - }; - - /// Check if the weak reference is still alive. - /// - /// The `self` parameter is the weak reference to check whether it is still alive. - /// Returns `true` if the weak reference is still alive, `false` otherwise. - /// False means that the value has been collected by the garbage collector. - /// ```motoko include=import - /// let obj = { x = 1 }; - /// let weakRef = WeakReference.allocate(obj); - /// let isLive = weakRef.isLive(); - /// assert isLive == true; - /// ``` - public func isLive(self : WeakReference) : Bool { - return Prim.isLive(self.ref) - }; - -} diff --git a/.mops/core@2.5.0/src/internal/BTreeHelper.mo b/.mops/core@2.5.0/src/internal/BTreeHelper.mo deleted file mode 100644 index 888087d..0000000 --- a/.mops/core@2.5.0/src/internal/BTreeHelper.mo +++ /dev/null @@ -1,412 +0,0 @@ -// Implementation is courtesy of Byron Becker. -// Source: https://github.com/canscale/StableHeapBTreeMap -// Copyright (c) 2022 Byron Becker. -// Distributed under Apache 2.0 license. -// With adjustments by the Motoko team. - -import VarArray "../VarArray"; -import Runtime "../Runtime"; - -module { - /// Inserts an element into a mutable array at a specific index, shifting all other elements over - /// - /// Parameters: - /// - /// array - the array being inserted into - /// insertElement - the element being inserted - /// insertIndex - the index at which the element will be inserted - /// currentLastElementIndex - the index of last **non-null** element in the array (used to start shifting elements over) - /// - /// Note: This assumes that there are nulls at the end of the array and that the array is not full. - /// If the array is already full, this function will overflow the array size when attempting to - /// insert and will cause the cansiter to trap - public func insertAtPosition(array : [var ?T], insertElement : ?T, insertIndex : Nat, currentLastElementIndex : Nat) { - // if inserting at the end of the array, don't need to do any shifting and can just insert and return - if (insertIndex == currentLastElementIndex + 1) { - array[insertIndex] := insertElement; - return - }; - - // otherwise, need to shift all of the elements at the end of the array over one by one until - // the insert index is hit. - var j = currentLastElementIndex; - label l loop { - array[j + 1] := array[j]; - if (j == insertIndex) { - array[j] := insertElement; - break l - }; - - j -= 1 - } - }; - - /// Splits the array into two halves as if the insert has occured, omitting the middle element and returning it so that it can - /// be promoted to the parent internal node. This is used when inserting an element into an array of elements that - /// is already full. - /// - /// Note: Use only when inserting an element into a FULL array & promoting the resulting midpoint element. - /// This is NOT the same as just splitting this array! - /// - /// Parameters: - /// - /// array - the array being split - /// insertElement - the element being inserted - /// insertIndex - the position/index that the insertElement should be inserted - public func insertOneAtIndexAndSplitArray(array : [var ?T], insertElement : T, insertIndex : Nat) : ([var ?T], T, [var ?T]) { - // split at the BTree order / 2 - let splitIndex = (array.size() + 1) / 2; - // this function assumes the the splitIndex is in the middle of the kvs array - trap otherwise - if (splitIndex > array.size()) { assert false }; - - let leftSplit = if (insertIndex < splitIndex) { - VarArray.tabulate( - array.size(), - func(i) { - // if below the split index - if (i < splitIndex) { - // if below the insert index, copy over - if (i < insertIndex) { array[i] } - // if less than the insert index, copy over the previous element (since the inserted element has taken up 1 extra slot) - else if (i > insertIndex) { array[i - 1] } - // if equal to the insert index add the element to be inserted to the left split - else { ?insertElement } - } else { null } - } - ) - } - // index >= splitIndex - else { - VarArray.tabulate( - array.size(), - func(i) { - // right biased splitting - if (i < splitIndex) { array[i] } else { null } - } - ) - }; - - let (rightSplit, middleElement) : ([var ?T], ?T) = - // if insert > split index, inserted element will be inserted into the right split - if (insertIndex > splitIndex) { - let right = VarArray.tabulate( - array.size(), - func(i) { - let adjIndex = i + splitIndex + 1; // + 1 accounts for the fact that the split element was part of the original array - if (adjIndex <= array.size()) { - if (adjIndex < insertIndex) { array[adjIndex] } else if (adjIndex > insertIndex) { - array[adjIndex - 1] - } else { ?insertElement } - } else { null } - } - ); - (right, array[splitIndex]) - } - // if inserted element was placed in the left split - else if (insertIndex < splitIndex) { - let right = VarArray.tabulate( - array.size(), - func(i) { - let adjIndex = i + splitIndex; - if (adjIndex < array.size()) { array[adjIndex] } else { null } - } - ); - (right, array[splitIndex - 1]) - } - // insertIndex == splitIndex - else { - let right = VarArray.tabulate( - array.size(), - func(i) { - let adjIndex = i + splitIndex; - if (adjIndex < array.size()) { array[adjIndex] } else { null } - } - ); - (right, ?insertElement) - }; - - switch (middleElement) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: insertOneAtIndexAndSplitArray, middle element of a BTree node should never be null") - }; - case (?el) { (leftSplit, el, rightSplit) } - } - }; - - /// Context of use: This function is used after inserting a child node into the full child of an internal node that is also full. - /// From the insertion, the full child is rebalanced and split, and then since the internal node is full, when replacing the two - /// halves of that rebalanced child into the internal node's children this causes a second split. This function takes in the - /// internal node's children, and the "rebalanced" split child nodes, as well as the index at which the "rebalanced" left and right - /// child will be inserted and replaces the original child with those two halves - /// - /// Note: Use when inserting two successive elements into a FULL array and splitting that array. - /// This is NOT the same as just splitting this array! - /// - /// Assumptions: this function also assumes that the children array is full (no nulls) - /// - /// Parameters: - /// - /// children - the internal node's children array being split - /// rebalancedChildIndex - the index used to mark where the rebalanced left and right children will be inserted - /// leftChildInsert - the rebalanced left child being inserted - /// rightChildInsert - the rebalanced right child being inserted - public func splitArrayAndInsertTwo(children : [var ?T], rebalancedChildIndex : Nat, leftChildInsert : T, rightChildInsert : T) : ([var ?T], [var ?T]) { - let splitIndex = children.size() / 2; - - let leftRebalancedChildren = VarArray.tabulate( - children.size(), - func(i) { - // only insert elements up to the split index and fill the rest of the children with nulls - if (i <= splitIndex) { - if (i < rebalancedChildIndex) { children[i] } - // insert the left and right rebalanced child halves if the rebalancedChildIndex comes before the splitIndex - else if (i == rebalancedChildIndex) { - ?leftChildInsert - } else if (i == rebalancedChildIndex + 1) { ?rightChildInsert } else { - children[i - 1] - } // i > rebalancedChildIndex - } else { null } - } - ); - - let rightRebalanceChildren : [var ?T] = - // Case 1: if both left and right rebalanced halves were inserted into the left child can just go from the split index onwards - if (rebalancedChildIndex + 1 <= splitIndex) { - VarArray.tabulate( - children.size(), - func(i) { - let adjIndex = i + splitIndex; - if (adjIndex < children.size()) { children[adjIndex] } else { null } - } - ) - } - // Case 2: if both left and right rebalanced halves will be inserted into the right child - else if (rebalancedChildIndex > splitIndex) { - var rebalanceOffset = 0; - VarArray.tabulate( - children.size(), - func(i) { - let adjIndex = i + splitIndex + 1; - if (adjIndex == rebalancedChildIndex) { ?leftChildInsert } else if (adjIndex == rebalancedChildIndex + 1) { - rebalanceOffset := 1; // after inserting both rebalanced children, any elements coming after are from the previous index - ?rightChildInsert - } else if (adjIndex <= children.size()) { - children[adjIndex - rebalanceOffset] - } else { null } - } - ) - } - // Case 3: if left rebalanced half was in left child, and right rebalanced half will be in right child - // rebalancedChildIndex == splitIndex - else { - VarArray.tabulate( - children.size(), - func(i) { - // first element is the right rebalanced half - if (i == 0) { ?rightChildInsert } else { - let adjIndex = i + splitIndex; - if (adjIndex < children.size()) { children[adjIndex] } else { - null - } - } - } - ) - }; - - (leftRebalancedChildren, rightRebalanceChildren) - }; - - /// Specific to the BTree delete implementation (assumes node ordering such that nulls come at the end of the array) - /// - /// Assumptions: - /// * All nulls come at the end of the array - /// * Assumes the delete index provided is correct and non null - will trap otherwise - /// * deleteIndex < array.size() - /// - /// Deletes an element from the the array, and then shifts all non-null elements coming after that deleted element by 1 - /// to the left. Returns the element that was deleted. - public func deleteAndShift(array : [var ?T], deleteIndex : Nat) : T { - var deleted : T = switch (array[deleteIndex]) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: deleteAndShift, an invalid/incorrect delete index was passed") - }; - case (?el) { el } - }; - - array[deleteIndex] := null; - - var i = deleteIndex + 1; - label l loop { - if (i >= array.size()) { break l }; - - switch (array[i]) { - case null { break l }; - case (?_) { - array[i - 1] := array[i] - } - }; - - i += 1 - }; - - array[i - 1] := null; - - deleted - }; - - // replaces two successive elements in the array with a single element and shifts all other elements to the left by 1 - public func replaceTwoWithElementAndShift(array : [var ?T], element : T, replaceIndex : Nat) { - array[replaceIndex] := ?element; - - var i = replaceIndex + 1; - let endShiftIndex : Nat = array.size() - 1; - while (i < endShiftIndex) { - switch (array[i]) { - case (?_) { array[i] := array[i + 1] }; - case null { return } - }; - - i += 1 - }; - - array[endShiftIndex] := null - }; - - /// BTree specific implementation - /// - /// In a single iteration insert at one position of the array while deleting at another position of the array, shifting all - /// elements as appropriate - /// - /// This is used when borrowing an element from an inorder predecessor/successor through the parent node - public func insertAtPostionAndDeleteAtPosition(array : [var ?T], insertElement : ?T, insertIndex : Nat, deleteIndex : Nat) : T { - var deleted : T = switch (array[deleteIndex]) { - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: insertAtPositionAndDeleteAtPosition, and incorrect delete index was passed") - }; // indicated an incorrect delete index was passed - trap - case (?el) { el } - }; - - // Example of this case: - // - // Insert Delete - // V V - //[var ?10, ?20, ?30, ?40, ?50] - if (insertIndex < deleteIndex) { - var i = deleteIndex; - while (i > insertIndex) { - array[i] := array[i - 1]; - i -= 1 - }; - - array[insertIndex] := insertElement - } - // Example of this case: - // - // Delete Insert - // V V - //[var ?10, ?20, ?30, ?40, ?50] - else if (insertIndex > deleteIndex) { - array[deleteIndex] := null; - var i = deleteIndex + 1; - label l loop { - if (i >= array.size()) { assert false; break l }; // TODO: remove? this should not happen since the insertIndex should get hit first? - - if (i == insertIndex) { - array[i - 1] := array[i]; - array[i] := insertElement; - break l - } else { - array[i - 1] := array[i] - }; - - i += 1 - }; - - } - // insertIndex == deleteIndex, can just do a swap - else { array[deleteIndex] := insertElement }; - - deleted - }; - - // which child the deletionIndex is referring to - public type DeletionSide = { #left; #right }; - - // merges a middle (parent) element with the left and right child arrays while deleting the element from the correct child by the deleteIndex passed - public func mergeParentWithChildrenAndDelete( - parentElement : ?T, - childCount : Nat, - leftChild : [var ?T], - rightChild : [var ?T], - deleteIndex : Nat, - deletionSide : DeletionSide - ) : ([var ?T], T) { - let mergedArray = VarArray.repeat(null, leftChild.size()); - var i = 0; - switch (deletionSide) { - case (#left) { - // BTree implementation expects the deleted element to exist - if null, traps - let deletedElement = switch (leftChild[deleteIndex]) { - case (?el) { el }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: mergeParentWithChildrenAndDelete, an invalid delete index was passed") - } - }; - - // copy over left child until deleted element is hit, then copy all elements after the deleted element - while (i < childCount) { - if (i < deleteIndex) { - mergedArray[i] := leftChild[i] - } else { - mergedArray[i] := leftChild[i + 1] - }; - i += 1 - }; - - // insert parent kv in the middle - mergedArray[childCount - 1] := parentElement; - - // copy over the rest of the right child elements - while (i < childCount * 2) { - mergedArray[i] := rightChild[i - childCount]; - i += 1 - }; - - (mergedArray, deletedElement) - }; - case (#right) { - // BTree implementation expects the deleted element to exist - if null, traps - let deletedElement = switch (rightChild[deleteIndex]) { - case (?el) { el }; - case null { - Runtime.trap("UNREACHABLE_ERROR: file a bug report! In internal/BTreeHelper: mergeParentWithChildrenAndDelete: element at deleted index must exist") - } - }; - // since deletion side is #right, can safely copy over all elements from the left child - while (i < childCount) { - mergedArray[i] := leftChild[i]; - i += 1 - }; - - // insert parent kv in the middle - mergedArray[childCount] := parentElement; - i += 1; - - var j = 0; - // copy over right child until deleted element is hit, then copy elements after the deleted element - while (i < childCount * 2) { - if (j < deleteIndex) { - mergedArray[i] := rightChild[j] - } else { - mergedArray[i] := rightChild[j + 1] - }; - i += 1; - j += 1 - }; - - (mergedArray, deletedElement) - } - } - }; - -} diff --git a/.mops/core@2.5.0/src/internal/PRNG.mo b/.mops/core@2.5.0/src/internal/PRNG.mo deleted file mode 100644 index 8a59861..0000000 --- a/.mops/core@2.5.0/src/internal/PRNG.mo +++ /dev/null @@ -1,76 +0,0 @@ -/// Collection of pseudo-random number generators -/// -/// The algorithms deliver deterministic statistical randomness, -/// not cryptographic randomness. -/// -/// Algorithm 1: 128-bit Seiran PRNG -/// See: https://github.com/andanteyk/prng-seiran -/// -/// Algorithm 2: SFC64 and SFC32 (Chris Doty-Humphrey’s Small Fast Chaotic PRNG) -/// See: https://numpy.org/doc/stable/reference/random/bit_generators/sfc64.html -/// -/// Copyright: 2023 MR Research AG -/// Main author: react0r-com -/// Contributors: Timo Hanke (timohanke) -import Nat "../Nat"; - -module { - /// Constructs an SFC 64-bit generator. - /// The recommended constructor arguments are: 24, 11, 3. - /// - /// Example: - /// ```motoko - /// import PRNG "mo:core/internal/PRNG"; - /// - /// let rng = PRNG.SFC64(24, 11, 3); - /// ``` - /// For convenience, the function `SFC64a()` returns a generator constructed - /// with the recommended parameter set (24, 11, 3). - public class SFC64(p : Nat64, q : Nat64, r : Nat64) { - // state - var a : Nat64 = 0; - var b : Nat64 = 0; - var c : Nat64 = 0; - var d : Nat64 = 0; - - /// Initializes the PRNG state with a particular seed - /// - /// Example: - /// ```motoko - public func init(seed : Nat64) = init3(seed, seed, seed); - - /// Initializes the PRNG state with a hardcoded seed. - /// No argument is required. - /// - /// Example: - public func initPre() = init(0xcafef00dbeef5eed); - - /// Initializes the PRNG state with three state variables - /// - /// Example: - public func init3(seed1 : Nat64, seed2 : Nat64, seed3 : Nat64) { - a := seed1; - b := seed2; - c := seed3; - d := 1; - - for (_ in Nat.range(0, 11)) ignore next() - }; - - /// Returns one output and advances the PRNG's state - /// - /// Example: - public func next() : Nat64 { - let tmp = a +% b +% d; - a := b ^ (b >> q); - b := c +% (c << r); - c := (c <<> p) +% tmp; - d +%= 1; - tmp - } - }; - - /// SFC64a is the same as numpy. - /// See: [sfc64_next()](https:///github.com/numpy/numpy/blob/b6d372c25fab5033b828dd9de551eb0b7fa55800/numpy/random/src/sfc64/sfc64.h#L28) - public func sfc64a() : SFC64 { SFC64(24, 11, 3) } -} diff --git a/.mops/core@2.5.0/src/internal/SortHelper.mo b/.mops/core@2.5.0/src/internal/SortHelper.mo deleted file mode 100644 index 2222e23..0000000 --- a/.mops/core@2.5.0/src/internal/SortHelper.mo +++ /dev/null @@ -1,1270 +0,0 @@ -import Runtime "../Runtime"; -import Order "../Order"; -import Prim "mo:⛔"; - -module { - let nat = Prim.nat32ToNat; - - // Must have: len <= 8 - // Use dest = buffer when sorting in place - public func insertionSortSmall(buffer : [var T], dest : [var T], compare : (T, T) -> Order.Order, newFrom : Nat32, len : Nat32) { - debug assert len > 0; - switch (len) { - case (1) { - let index0 = nat(newFrom); - dest[index0] := buffer[index0] - }; - case (2) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let t0 = buffer[index0]; - let t1 = buffer[index1]; - switch (compare(t1, t0)) { - case (#less) { - dest[index0] := t1; - dest[index1] := t0 - }; - case (_) { - dest[index0] := t0; - dest[index1] := t1 - } - } - }; - case (3) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - let t2 = buffer[index2]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - - switch (compare(t2, t1)) { - case (#less) { - switch (compare(t2, t0)) { - case (#less) { - dest[index0] := t2; - dest[index1] := t0; - dest[index2] := t1 - }; - case (_) { - dest[index0] := t0; - dest[index1] := t2; - dest[index2] := t1 - } - } - }; - case (_) { - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2 - } - } - }; - case (4) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - - switch (compare(t3, t2)) { - case (#less) { - tv := t3; - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3 - }; - case (5) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - let index4 = nat(newFrom +% 4); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - var t4 = buffer[index4]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3; - dest[index4] := t4 - }; - case (6) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - let index4 = nat(newFrom +% 4); - let index5 = nat(newFrom +% 5); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - var t4 = buffer[index4]; - var t5 = buffer[index5]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3; - dest[index4] := t4; - dest[index5] := t5 - }; - case (7) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - let index4 = nat(newFrom +% 4); - let index5 = nat(newFrom +% 5); - let index6 = nat(newFrom +% 6); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - var t4 = buffer[index4]; - var t5 = buffer[index5]; - var t6 = buffer[index6]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - tv := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3; - dest[index4] := t4; - dest[index5] := t5; - dest[index6] := t6 - }; - case (8) { - let index0 = nat(newFrom); - let index1 = nat(newFrom +% 1); - let index2 = nat(newFrom +% 2); - let index3 = nat(newFrom +% 3); - let index4 = nat(newFrom +% 4); - let index5 = nat(newFrom +% 5); - let index6 = nat(newFrom +% 6); - let index7 = nat(newFrom +% 7); - var t0 = buffer[index0]; - var t1 = buffer[index1]; - var t2 = buffer[index2]; - var t3 = buffer[index3]; - var t4 = buffer[index4]; - var t5 = buffer[index5]; - var t6 = buffer[index6]; - var t7 = buffer[index7]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - tv := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) {} - }; - tv := t7; - switch (compare(tv, t6)) { - case (#less) { - t7 := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) { t6 := tv } - } - }; - case (_) {} - }; - - dest[index0] := t0; - dest[index1] := t1; - dest[index2] := t2; - dest[index3] := t3; - dest[index4] := t4; - dest[index5] := t5; - dest[index6] := t6; - dest[index7] := t7 - }; - case (_) Runtime.trap("insertionSortSmall for len > 8 is not implemented.") - } - }; - - // sort from buffer to dest array at the given offset - public func insertionSortSmallMove(buffer : [var T], dest : [var T], compare : (T, T) -> Order.Order, newFrom : Nat32, len : Nat32, offset : Nat32) { - debug assert len > 0; - switch (len) { - case (1) { - dest[nat(offset)] := buffer[nat(newFrom)] - }; - case (2) { - let t0 = buffer[nat(newFrom)]; - let t1 = buffer[nat(newFrom +% 1)]; - switch (compare(t1, t0)) { - case (#less) { - dest[nat(offset)] := t1; - dest[nat(offset +% 1)] := t0 - }; - case (_) { - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1 - } - } - }; - case (3) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - let t2 = buffer[nat(newFrom +% 2)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - - switch (compare(t2, t1)) { - case (#less) { - switch (compare(t2, t0)) { - case (#less) { - dest[nat(offset)] := t2; - dest[nat(offset +% 1)] := t0; - dest[nat(offset +% 2)] := t1 - }; - case (_) { - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t2; - dest[nat(offset +% 2)] := t1 - } - } - }; - case (_) { - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2 - } - } - }; - case (4) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - - switch (compare(t3, t2)) { - case (#less) { - tv := t3; - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3 - }; - case (5) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - var t4 = buffer[nat(newFrom +% 4)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3; - dest[nat(offset +% 4)] := t4 - }; - case (6) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - var t4 = buffer[nat(newFrom +% 4)]; - var t5 = buffer[nat(newFrom +% 5)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3; - dest[nat(offset +% 4)] := t4; - dest[nat(offset +% 5)] := t5 - }; - case (7) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - var t4 = buffer[nat(newFrom +% 4)]; - var t5 = buffer[nat(newFrom +% 5)]; - var t6 = buffer[nat(newFrom +% 6)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - tv := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3; - dest[nat(offset +% 4)] := t4; - dest[nat(offset +% 5)] := t5; - dest[nat(offset +% 6)] := t6 - }; - case (8) { - var t0 = buffer[nat(newFrom)]; - var t1 = buffer[nat(newFrom +% 1)]; - var t2 = buffer[nat(newFrom +% 2)]; - var t3 = buffer[nat(newFrom +% 3)]; - var t4 = buffer[nat(newFrom +% 4)]; - var t5 = buffer[nat(newFrom +% 5)]; - var t6 = buffer[nat(newFrom +% 6)]; - var t7 = buffer[nat(newFrom +% 7)]; - - switch (compare(t1, t0)) { - case (#less) { - let v = t1; - t1 := t0; - t0 := v - }; - case (_) {} - }; - var tv = t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) {} - }; - tv := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) {} - }; - tv := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) {} - }; - tv := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) {} - }; - tv := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) {} - }; - tv := t7; - switch (compare(tv, t6)) { - case (#less) { - t7 := t6; - switch (compare(tv, t5)) { - case (#less) { - t6 := t5; - switch (compare(tv, t4)) { - case (#less) { - t5 := t4; - switch (compare(tv, t3)) { - case (#less) { - t4 := t3; - switch (compare(tv, t2)) { - case (#less) { - t3 := t2; - switch (compare(tv, t1)) { - case (#less) { - t2 := t1; - switch (compare(tv, t0)) { - case (#less) { t1 := t0; t0 := tv }; - case (_) { t1 := tv } - } - }; - case (_) { t2 := tv } - } - }; - case (_) { t3 := tv } - } - }; - case (_) { t4 := tv } - } - }; - case (_) { t5 := tv } - } - }; - case (_) { t6 := tv } - } - }; - case (_) {} - }; - - dest[nat(offset)] := t0; - dest[nat(offset +% 1)] := t1; - dest[nat(offset +% 2)] := t2; - dest[nat(offset +% 3)] := t3; - dest[nat(offset +% 4)] := t4; - dest[nat(offset +% 5)] := t5; - dest[nat(offset +% 6)] := t6; - dest[nat(offset +% 7)] := t7 - }; - case (_) Runtime.trap("insertionSortSmall for len > 8 is not implemented.") - } - } -} diff --git a/.mops/core@2.5.0/src/pure/List.mo b/.mops/core@2.5.0/src/pure/List.mo deleted file mode 100644 index c0d36f1..0000000 --- a/.mops/core@2.5.0/src/pure/List.mo +++ /dev/null @@ -1,1114 +0,0 @@ -/// Purely-functional, singly-linked list data structure. -/// This module provides immutable lists with efficient prepend and traversal operations. -/// -/// A list of type `List` is either `null` or an optional pair of a value of type `T` and a tail, itself of type `List`. -/// -/// To use this library, import it using: -/// -/// ```motoko name=import -/// import List "mo:core/pure/List"; -/// ``` - -import { Array_tabulate } "mo:⛔"; -import Array "../Array"; -import Iter "../Iter"; -import Order "../Order"; -import Result "../Result"; -import { trap } "../Runtime"; -import Types "../Types"; -import Runtime "../Runtime"; - -module { - - /// @deprecated M0235 - public type List = Types.Pure.List; - - /// Create an empty list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// assert List.empty() == null; - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func empty() : List = null; - - /// Check whether a list is empty and return true if the list is empty. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// assert List.isEmpty(null); - /// assert not List.isEmpty(?(1, null)); - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func isEmpty(self : List) : Bool = switch self { - case null true; - case _ false - }; - - /// Return the length of the list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, null)); - /// assert List.size(list) == 2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func size(self : List) : Nat = ( - func go(n : Nat, list : List) : Nat = switch list { - case (?(_, t)) go(n + 1, t); - case null n - } - )(0, self); - - /// Check whether the list contains a given value. Uses the provided equality function to compare values. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.contains(list, Nat.equal, 2); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equal` runs in O(1) time and space. - public func contains(self : List, equal : (implicit : (T, T) -> Bool), item : T) : Bool = switch self { - case (?(h, t)) equal(h, item) or contains(t, equal, item); - case _ false - }; - - /// Access any item in a list, zero-based. - /// - /// NOTE: Indexing into a list is a linear operation, and usually an - /// indication that a list might not be the best data structure - /// to use. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, null)); - /// assert List.get(list, 1) == ?1; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func get(self : List, n : Nat) : ?T = switch self { - case (?(h, t)) if (n == 0) ?h else get(t, n - 1 : Nat); - case null null - }; - - /// Add `item` to the head of `list`, and return the new list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// assert List.pushFront(null, 0) == ?(0, null); - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func pushFront(self : List, item : T) : List = ?(item, self); - - /// Return the last element of the list, if present. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, null)); - /// assert List.last(list) == ?1; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - public func last(self : List) : ?T = switch self { - case (?(h, null)) ?h; - case null null; - case (?(_, t)) last t - }; - - /// Remove the head of the list, returning the optioned head and the tail of the list in a pair. - /// Returns `(null, null)` if the list is empty. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, null)); - /// assert List.popFront(list) == (?0, ?(1, null)); - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func popFront(self : List) : (?T, List) = switch self { - case null (null, null); - case (?(h, t)) (?h, t) - }; - - /// Reverses the list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.reverse(list) == ?(2, ?(1, ?(0, null))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func reverse(self : List) : List = ( - func go(acc : List, list : List) : List = switch list { - case (?(h, t)) go(?(h, acc), t); - case null acc - } - )(null, self); - - /// Call the given function for its side effect, with each list element in turn. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// var sum = 0; - /// List.forEach(list, func n = sum += n); - /// assert sum == 3; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func forEach(self : List, f : T -> ()) = switch self { - case (?(h, t)) { f h; forEach(t, f) }; - case null () - }; - - /// Call the given function `f` on each list element and collect the results - /// in a new list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.map(list, Nat.toText) == ?("0", ?("1", ?("2", null))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func map(self : List, f : T1 -> T2) : List = ( - func go(list : List, f : T1 -> T2, acc : List) : List = switch list { - case (?(h, t)) go(t, f, ?(f h, acc)); - case null reverse acc - } - )(self, f, null); - - /// Create a new list with only those elements of the original list for which - /// the given function (often called the _predicate_) returns true. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.filter(list, func n = n != 1) == ?(0, ?(2, null)); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func filter(self : List, f : T -> Bool) : List = ( - func go(list : List, f : T -> Bool, acc : List) : List = switch list { - case (?(h, t)) if (f h) go(t, f, ?(h, acc)) else go(t, f, acc); - case null reverse acc - } - )(self, f, null); - - /// Call the given function on each list element, and collect the non-null results - /// in a new list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.filterMap( - /// list, - /// func n = if (n > 1) ?(n * 2) else null - /// ) == ?(4, ?(6, null)); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func filterMap(self : List, f : T -> ?R) : List = ( - func go(list : List, f : T -> ?R, acc : List) : List = switch list { - case (?(h, t)) switch (f h) { - case null go(t, f, acc); - case (?r) go(t, f, ?(r, acc)) - }; - case null reverse acc - } - )(self, f, null); - - /// Maps a `Result`-returning function `f` over a `List` and returns either - /// the first error or a list of successful values. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.mapResult( - /// list, - /// func n = if (n > 0) #ok(n * 2) else #err "Some element is zero" - /// ) == #ok(?(2, ?(4, ?(6, null)))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func mapResult(self : List, f : T -> Result.Result) : Result.Result, E> = ( - func rev(acc : List, list : List, f : T -> Result.Result) : Result.Result, E> = switch list { - case (?(h, t)) switch (f h) { - case (#ok fh) rev(?(fh, acc), t, f); - case (#err e) #err e - }; - case null #ok(reverse acc) - } - )(null, self, f); - - /// Create two new lists from the results of a given function (`f`). - /// The first list only includes the elements for which the given - /// function `f` returns true and the second list only includes - /// the elements for which the function returns false. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.partition(list, func n = n != 1) == (?(0, ?(2, null)), ?(1, null)); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func partition(self : List, f : T -> Bool) : (List, List) = ( - func go(list : List, f : T -> Bool, acc1 : List, acc2 : List) : (List, List) = switch list { - case (?(h, t)) if (f h) go(t, f, ?(h, acc1), acc2) else go(t, f, acc1, ?(h, acc2)); - case null (reverse acc1, reverse acc2) - } - )(self, f, null, null); - - /// Append the elements from one list to another list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list1 = ?(0, ?(1, ?(2, null))); - /// let list2 = ?(3, ?(4, ?(5, null))); - /// assert List.concat(list1, list2) == ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))); - /// } - /// ``` - /// - /// Runtime: O(size(l)) - /// - /// Space: O(size(l)) - public func concat(self : List, other : List) : List = revAppend(reverse self, other); - - /// Flatten, or repatedly concatenate, an iterator of lists as a list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let lists = [ ?(0, ?(1, ?(2, null))), - /// ?(3, ?(4, ?(5, null))) ]; - /// assert List.join(lists |> Iter.fromArray(_)) == ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))); - /// } - /// ``` - /// - /// Runtime: O(size*size) - /// - /// Space: O(size*size) - public func join(iter : Iter.Iter>) : List { - var acc : List = null; - for (list in iter) { - acc := revAppend(list, acc) - }; - reverse acc - }; - - /// Flatten, or repatedly concatenate, a list of lists as a list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let lists = ?(?(0, ?(1, ?(2, null))), - /// ?(?(3, ?(4, ?(5, null))), - /// null)); - /// assert List.flatten(lists) == ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null)))))); - /// } - /// ``` - /// - /// Runtime: O(size*size) - /// - /// Space: O(size*size) - public func flatten(self : List>) : List = ( - func go(lists : List>, acc : List) : List = switch lists { - case (?(list, t)) go(t, revAppend(list, acc)); - case null reverse acc - } - )(self, null); - - /// Returns the first `n` elements of the given list. - /// If the given list has fewer than `n` elements, this function returns - /// a copy of the full input list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.take(list, 2) == ?(0, ?(1, null)); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - public func take(self : List, n : Nat) : List = ( - func go(n : Nat, list : List, acc : List) : List = if (n == 0) reverse acc else switch list { - case (?(h, t)) go(n - 1 : Nat, t, ?(h, acc)); - case null reverse acc - } - )(n, self, null); - - /// Drop the first `n` elements from the given list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.drop(list, 2) == ?(2, null); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(1) - public func drop(self : List, n : Nat) : List = if (n == 0) self else switch self { - case (?(_, t)) drop(t, n - 1 : Nat); - case null null - }; - - /// Collapses the elements in `list` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// left to right. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.foldLeft( - /// list, - /// "", - /// func (acc, x) = acc # Nat.toText(x) - /// ) == "123"; - /// } - /// ``` - /// - /// Runtime: O(size(list)) - /// - /// Space: O(1) heap, O(1) stack - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldLeft(self : List, base : A, combine : (A, T) -> A) : A = switch self { - case null base; - case (?(h, t)) foldLeft(t, combine(base, h), combine) - }; - - /// Collapses the elements in `buffer` into a single value by starting with `base` - /// and progessively combining elements into `base` with `combine`. Iteration runs - /// right to left. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.foldRight( - /// list, - /// "", - /// func (x, acc) = Nat.toText(x) # acc - /// ) == "123"; - /// } - /// ``` - /// - /// Runtime: O(size(list)) - /// - /// Space: O(1) heap, O(size(list)) stack - /// - /// *Runtime and space assumes that `combine` runs in O(1) time and space. - public func foldRight(self : List, base : A, combine : (T, A) -> A) : A = ( - func go(list : List, base : A, combine : (T, A) -> A) : A = switch list { - case null base; - case (?(h, t)) go(t, combine(h, base), combine) - } - )(reverse self, base, combine); - - /// Return the first element for which the given predicate `f` is true, - /// if such an element exists. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.find(list, func n = n > 1) == ?2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func find(self : List, f : T -> Bool) : ?T = switch self { - case null null; - case (?(h, t)) if (f h) ?h else find(t, f) - }; - - /// Return the first index for which the given predicate `f` is true. - /// If no element satisfies the predicate, returns null. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.fromArray(['A', 'B', 'C', 'D']); - /// let found = List.findIndex(list, func(x) { x == 'C' }); - /// assert found == ?2; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func findIndex(self : List, f : T -> Bool) : ?Nat { - findIndex_(self, 0, f) - }; - - private func findIndex_(self : List, index : Nat, f : T -> Bool) : ?Nat = switch self { - case null null; - case (?(h, t)) if (f h) ?index else findIndex_(t, index + 1, f) - }; - - /// Return true if the given predicate `f` is true for all list - /// elements. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert not List.all(list, func n = n > 1); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func all(self : List, f : T -> Bool) : Bool = switch self { - case null true; - case (?(h, t)) f h and all(t, f) - }; - - /// Return true if there exists a list element for which - /// the given predicate `f` is true. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.any(list, func n = n > 1); - /// } - /// ``` - /// - /// Runtime: O(size(list)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func any(self : List, f : T -> Bool) : Bool = switch self { - case null false; - case (?(h, t)) f h or any(t, f) - }; - - /// Merge two ordered lists into a single ordered list. - /// This function requires both list to be ordered as specified - /// by the given relation `compare`. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list1 = ?(1, ?(2, ?(4, null))); - /// let list2 = ?(2, ?(4, ?(6, null))); - /// assert List.merge(list1, list2, Nat.compare) == ?(1, ?(2, ?(2, ?(4, ?(4, ?(6, null)))))); - /// } - /// ``` - /// - /// Runtime: O(size(l1) + size(l2)) - /// - /// Space: O(size(l1) + size(l2)) - /// - /// *Runtime and space assumes that `lessThanOrEqual` runs in O(1) time and space. - public func merge(self : List, other : List, compare : (implicit : (T, T) -> Order.Order)) : List = ( - func go(list1 : List, list2 : List, compare : (T, T) -> Order.Order, acc : List) : List = switch (list1, list2) { - case ((null, l) or (l, null)) reverse(revAppend(l, acc)); - case (?(h1, t1), ?(h2, t2)) switch (compare(h1, h2)) { - case (#less or #equal) go(t1, list2, compare, ?(h1, acc)); - case (#greater) go(list1, t2, compare, ?(h2, acc)) - } - } - )(self, other, compare, null); - - /// Check if two lists are equal using the given equality function to compare elements. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list1 = ?(1, ?(2, null)); - /// let list2 = ?(1, ?(2, null)); - /// assert List.equal(list1, list2, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that `equalItem` runs in O(1) time and space. - public func equal(self : List, other : List, equalItem : (implicit : (equal : (T, T) -> Bool))) : Bool = switch (self, other) { - case (null, null) true; - case (?(h1, t1), ?(h2, t2)) equalItem(h1, h2) and equal(t1, t2, equalItem); - case _ false - }; - - /// Compare two lists using lexicographic ordering specified by argument function `compareItem`. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list1 = ?(1, ?(2, null)); - /// let list2 = ?(3, ?(4, null)); - /// assert List.compare(list1, list2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: O(size(l1)) - /// - /// Space: O(1) - /// - /// *Runtime and space assumes that argument `compare` runs in O(1) time and space. - public func compare(self : List, other : List, compareItem : (implicit : (compare : (T, T) -> Order.Order))) : Order.Order = switch (self, other) { - case (?(h1, t1), ?(h2, t2)) switch (compareItem(h1, h2)) { - case (#equal) compare(t1, t2, compareItem); - case o o - }; - case (null, null) #equal; - case (null, _) #less; - case _ #greater - }; - - /// Generate a list based on a length and a function that maps from - /// a list index to a list element. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.tabulate(3, func n = n * 2); - /// assert list == ?(0, ?(2, ?(4, null))); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func tabulate(n : Nat, f : Nat -> T) : List { - var i = 0; - var l : List = null; - while (i < n) { - l := ?(f i, l); - i += 1 - }; - reverse l - }; - - /// Create a list with exactly one element. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// assert List.singleton(0) == ?(0, null); - /// } - /// ``` - /// - /// Runtime: O(1) - /// - /// Space: O(1) - public func singleton(item : T) : List = ?(item, null); - - /// Create a list of the given length with the same value in each position. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.repeat('a', 3); - /// assert list == ?('a', ?('a', ?('a', null))); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - public func repeat(item : T, n : Nat) : List { - var res : List = null; - var i : Int = n; - while (i != 0) { - i -= 1; - res := ?(item, res) - }; - res - }; - - /// Create a list of pairs from a pair of lists. - /// - /// If the given lists have different lengths, then the created list will have a - /// length equal to the length of the smaller list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list1 = ?(0, ?(1, ?(2, null))); - /// let list2 = ?("0", ?("1", null)); - /// assert List.zip(list1, list2) == ?((0, "0"), ?((1, "1"), null)); - /// } - /// ``` - /// - /// Runtime: O(min(size(xs), size(ys))) - /// - /// Space: O(min(size(xs), size(ys))) - public func zip(self : List, other : List) : List<(T, U)> = zipWith(self, other, func(x, y) = (x, y)); - - /// Create a list in which elements are created by applying function `f` to each pair `(x, y)` of elements - /// occuring at the same position in list `xs` and list `ys`. - /// - /// If the given lists have different lengths, then the created list will have a - /// length equal to the length of the smaller list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// import Char "mo:core/Char"; - /// - /// persistent actor { - /// let list1 = ?(0, ?(1, ?(2, null))); - /// let list2 = ?('a', ?('b', null)); - /// assert List.zipWith( - /// list1, - /// list2, - /// func (n, c) = Nat.toText(n) # Char.toText(c) - /// ) == ?("0a", ?("1b", null)); - /// } - /// ``` - /// - /// Runtime: O(min(size(xs), size(ys))) - /// - /// Space: O(min(size(xs), size(ys))) - /// - /// *Runtime and space assumes that `f` runs in O(1) time and space. - public func zipWith(self : List, other : List, f : (T, U) -> V) : List = ( - func go(list1 : List, list2 : List, f : (T, U) -> V, acc : List) : List = switch (list1, list2) { - case ((null, _) or (_, null)) reverse acc; - case (?(h1, t1), ?(h2, t2)) go(t1, t2, f, ?(f(h1, h2), acc)) - } - )(self, other, f, null); - - /// Split the given list at the given zero-based index. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, null))); - /// assert List.split(list, 2) == (?(0, ?(1, null)), ?(2, null)); - /// } - /// ``` - /// - /// Runtime: O(n) - /// - /// Space: O(n) - public func split(self : List, n : Nat) : (List, List) { - func go(n : Nat, list : List, acc : List) : (List, List) = if (n == 0) (reverse acc, list) else switch list { - case (?(h, t)) go(n - 1 : Nat, t, ?(h, acc)); - case null (reverse acc, null) - }; - go(n, self, null) - }; - - /// Split the given list into chunks of length `n`. - /// The last chunk will be shorter if the length of the given list - /// does not divide by `n` evenly. Traps if `n` = 0. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// assert List.chunks(list, 2) == ?(?(0, ?(1, null)), ?(?(2, ?(3, null)), ?(?(4, null), null))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func chunks(self : List, n : Nat) : List> { - if (n == 0) trap "pure/List.chunks()"; - func go(list : List, n : Nat, acc : List>) : List> = switch (split(list, n)) { - case (null, _) reverse acc; - case (pre, null) reverse(?(pre, acc)); - case (pre, post) go(post, n, ?(pre, acc)) - }; - go(self, n, null) - }; - - /// Returns an iterator to the elements in the list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = List.fromArray([3, 1, 4]); - /// var text = ""; - /// for (item in List.values(list)) { - /// text #= Nat.toText(item); - /// }; - /// assert text == "314"; - /// } - /// ``` - public func values(self : List) : Iter.Iter = object { - var l = self; - public func next() : ?T = switch l { - case null null; - case (?(h, t)) { - l := t; - ?h - } - } - }; - - /// Returns an iterator to the `(index, element)` pairs in the list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = List.fromArray([3, 1, 4]); - /// var text = ""; - /// for ((index, element) in List.enumerate(list)) { - /// text #= Nat.toText(index); - /// }; - /// assert text == "012"; - /// } - /// ``` - public func enumerate(self : List) : Iter.Iter<(Nat, T)> = object { - var i = 0; - var l = self; - public func next() : ?(Nat, T) = switch l { - case null null; - case (?(h, t)) { - l := t; - let index = i; - i += 1; - ?(index, h) - } - } - }; - - /// Convert an array into a list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.fromArray([0, 1, 2, 3, 4]); - /// assert list == ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromArray(array : [T]) : List { - func go(from : Nat) : List = if (from < array.size()) ?(array.get from, go(from + 1)) else null; - go 0 - }; - - /// Convert a mutable array into a list. - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.fromVarArray([var 0, 1, 2, 3, 4]); - /// assert list == ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromVarArray(array : [var T]) : List = fromArray(Array.fromVarArray(array)); - - /// Create an array from a list. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Array "mo:core/Array"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let array = List.toArray(?(0, ?(1, ?(2, ?(3, ?(4, null)))))); - /// assert Array.equal(array, [0, 1, 2, 3, 4], Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toArray(self : List) : [T] { - var l = self; - Array_tabulate(size self, func _ { let ?(h, t) = l else Runtime.trap("List.toArray(): unreachable"); l := t; h }) - }; - - /// Create a mutable array from a list. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Array "mo:core/Array"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let array = List.toVarArray(?(0, ?(1, ?(2, ?(3, ?(4, null)))))); - /// assert Array.equal(Array.fromVarArray(array), [0, 1, 2, 3, 4], Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toVarArray(self : List) : [var T] = Array.toVarArray(toArray(self)); - - /// Create a list from an iterator, consuming the iterator. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// let list = List.fromIter([0, 1, 2, 3, 4].vals()); - /// assert list == ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromIter(iter : Iter.Iter) : List { - var result : List = null; - for (x in iter) { - result := ?(x, result) - }; - reverse result - }; - - /// Convert an iterator to a list, consuming the iterator. - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// - /// persistent actor { - /// transient let iter = [0, 1, 2, 3, 4].vals(); - /// - /// let list = iter.toList(); - /// - /// assert list == ?(0, ?(1, ?(2, ?(3, ?(4, null))))); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toList(self : Iter.Iter) : List { - fromIter(self) - }; - - /// Convert a list to a text representation using the provided function to convert each element to text. - /// The resulting text will be in the format "[element1, element2, ...]". - /// - /// Example: - /// ```motoko - /// import List "mo:core/pure/List"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let list = ?(1, ?(2, ?(3, null))); - /// assert List.toText(list, Nat.toText) == "PureList[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toText(self : List, f : (implicit : T -> Text)) : Text { - var text = "PureList["; - var first = true; - forEach( - self, - func(item : T) { - if first { - first := false - } else { - text #= ", " - }; - text #= f item - } - ); - text # "]" - }; - - // revAppend([x1 .. xn], [y1 .. ym]) = [xn .. x1, y1 .. ym] - func revAppend(l : List, m : List) : List = switch l { - case (?(h, t)) revAppend(t, ?(h, m)); - case null m - } -} diff --git a/.mops/core@2.5.0/src/pure/Map.mo b/.mops/core@2.5.0/src/pure/Map.mo deleted file mode 100644 index ebddab3..0000000 --- a/.mops/core@2.5.0/src/pure/Map.mo +++ /dev/null @@ -1,1563 +0,0 @@ -/// Immutable, ordered key-value maps. -/// -/// The map type is stable whenever the key and value types are stable, allowing -/// map values to be stored in stable variables. -/// -/// Keys are ordered by an explicit `compare` function, which *must* be the same -/// across all operations on a given map. -/// -/// -/// Example: -/// ```motoko -/// import Map "mo:core/pure/Map"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// // creation -/// let empty = Map.empty(); -/// // insertion -/// let map1 = Map.add(empty, Nat.compare, 0, "Zero"); -/// // retrieval -/// assert Map.get(empty, Nat.compare, 0) == null; -/// assert Map.get(map1, Nat.compare, 0) == ?"Zero"; -/// // removal -/// let map2 = Map.remove(map1, Nat.compare, 0); -/// assert not Map.isEmpty(map1); -/// assert Map.isEmpty(map2); -/// } -/// ``` -/// -/// The internal representation is a red-black tree. -/// -/// A red-black tree is a balanced binary search tree ordered by the keys. -/// -/// The tree data structure internally colors each of its nodes either red or black, -/// and uses this information to balance the tree during the modifying operations. -/// -/// Performance: -/// * Runtime: `O(log(n))` worst case cost per insertion, removal, and retrieval operation. -/// * Space: `O(n)` for storing the entire tree. -/// `n` denotes the number of key-value entries (i.e. nodes) stored in the tree. -/// -/// Note: -/// * Map operations, such as retrieval, insertion, and removal create `O(log(n))` temporary objects that become garbage. -/// -/// Credits: -/// -/// The core of this implementation is derived from: -/// -/// * Ken Friis Larsen's [RedBlackMap.sml](https://github.com/kfl/mosml/blob/master/src/mosmllib/Redblackmap.sml), which itself is based on: -/// * Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](http://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html). - -import Order "../Order"; -import Iter "../Iter"; -import Types "../Types"; -import Runtime "../Runtime"; - -// TODO: inline Internal? -// TODO: Do we want clone or clear, just to match imperative API? -// inline Tree type, remove Types.Pure.Tree? - -module { - - /// @deprecated M0235 - public type Map = Types.Pure.Map; - - type Tree = Types.Pure.Map.Tree; - - /// Create a new empty immutable key-value map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.empty(); - /// assert Map.size(map) == 0; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Map { - Internal.empty() - }; - - /// Determines whether a key-value map is empty. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map0 = Map.empty(); - /// let map1 = Map.add(map0, Nat.compare, 0, "Zero"); - /// - /// assert Map.isEmpty(map0); - /// assert not Map.isEmpty(map1); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func isEmpty(self : Map) : Bool { - self.size == 0 - }; - - /// Determine the size of the map as the number of key-value entries. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.size(map) == 3; - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func size(self : Map) : Nat = self.size; - - /// Test whether the map `map`, ordered by `compare`, contains a binding for the given `key`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.containsKey(map, Nat.compare, 1); - /// assert not Map.containsKey(map, Nat.compare, 42); - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func containsKey(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : Bool = Internal.contains(self.root, compare, key); - - /// Given, `map` ordered by `compare`, return the value associated with key `key` if present and `null` otherwise. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.get(map, Nat.compare, 1) == ?"One"; - /// assert Map.get(map, Nat.compare, 42) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func get(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : ?V = Internal.get(self.root, compare, key); - - /// Given `map` ordered by `compare`, insert a mapping from `key` to `value`. - /// Returns the modified map and `true` if the key is new to map, otherwise `false`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = Map.empty(); - /// - /// do { - /// let (map1, new1) = Map.insert(map0, Nat.compare, 0, "Zero"); - /// assert Iter.toArray(Map.entries(map1)) == [(0, "Zero")]; - /// assert new1; - /// let (map2, new2) = Map.insert(map1, Nat.compare, 0, "Nil"); - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Nil")]; - /// assert not new2 - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `m := Map.add(m, cmp, k, v)`) - /// causes collecting `O(log(n))` nodes. - public func insert(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : (Map, Bool) { - switch (swap(self, compare, key, value)) { - case (map1, null) (map1, true); - case (map1, _) (map1, false) - } - }; - - /// Given `map` ordered by `compare`, add a new mapping from `key` to `value`. - /// Replaces any existing entry with key `key`. - /// Returns the modified map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// var map = Map.empty(); - /// - /// map := Map.add(map, Nat.compare, 0, "Zero"); - /// map := Map.add(map, Nat.compare, 1, "One"); - /// map := Map.add(map, Nat.compare, 0, "Nil"); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Nil"), (1, "One")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `m := Map.add(m, cmp, k, v)`) - /// causes collecting `O(log(n))` nodes. - public func add(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : Map { - swap(self, compare, key, value).0 - }; - - /// Given `map` ordered by `compare`, add a mapping from `key` to `value`. Overwrites any existing entry with key `key`. - /// Returns the modified map and the previous value associated with key `key` - /// or `null` if no such value exists. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// do { - /// let (map1, old1) = Map.swap(map0, Nat.compare, 0, "Nil"); - /// assert Iter.toArray(Map.entries(map1)) == [(0, "Nil"), (1, "One"), (2, "Two")]; - /// assert old1 == ?"Zero"; - /// - /// let (map2, old2) = Map.swap(map0, Nat.compare, 3, "Three"); - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Zero"), (1, "One"), (2, "Two"), (3, "Three")]; - /// assert old2 == null; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `m := Map.swap(m, Nat.compare, k, v).0`) - /// causes collecting `O(log(n))` nodes. - public func swap(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : (Map, ?V) { - switch (Internal.swap(self.root, compare, key, value)) { - case (t, null) { ({ root = t; size = self.size + 1 }, null) }; - case (t, v) { ({ root = t; size = self.size }, v) } - } - }; - - /// Overwrites the value of an existing key and returns the updated map and previous value. - /// If the key does not exist, returns the original map and `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let singleton = Map.singleton(0, "Zero"); - /// - /// do { - /// let (map1, prev1) = Map.replace(singleton, Nat.compare, 0, "Nil"); // overwrites the value for existing key. - /// assert prev1 == ?"Zero"; - /// assert Map.get(map1, Nat.compare, 0) == ?"Nil"; - /// - /// let (map2, prev2) = Map.replace(map1, Nat.compare, 1, "One"); // no effect, key is absent - /// assert prev2 == null; - /// assert Map.get(map2, Nat.compare, 1) == null; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func replace(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K, value : V) : (Map, ?V) { - // TODO: Could be optimized in future - if (containsKey(self, compare, key)) { - swap(self, compare, key, value) - } else { (self, null) } - }; - - /// Given a `map`, ordered by `compare`, deletes any entry for `key` from `map`. - /// Has no effect if `key` is not present in the map. - /// Returns the updated map. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = - /// Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// let map1 = Map.remove(map0, Nat.compare, 1); - /// assert Iter.toArray(Map.entries(map1)) == [(0, "Zero"), (2, "Two")]; - /// let map2 = Map.remove(map0, Nat.compare, 42); - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `map := Map.delete(map, compare, k).0`) - /// causes collecting `O(log(n))` nodes. - public func remove(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : Map { - switch (Internal.remove(self.root, compare, key)) { - case (_, null) self; - case (t, ?_) { { root = t; size = self.size - 1 } } - } - }; - - /// Given a `map`, ordered by `compare`, deletes any entry for `key` from `map`. - /// Has no effect if `key` is not present in the map. - /// Returns the updated map and `true` if the `key` was present in `map`, otherwise `false`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = - /// Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// do { - /// let (map1, pres1) = Map.delete(map0, Nat.compare, 1); - /// assert Iter.toArray(Map.entries(map1)) == [(0, "Zero"), (2, "Two")]; - /// assert pres1; - /// let (map2, pres2) = Map.delete(map0, Nat.compare, 42); - /// assert not pres2; - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `map := Map.delete(map, compare, k).0`) - /// causes collecting `O(log(n))` nodes. - public func delete(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : (Map, Bool) { - switch (Internal.remove(self.root, compare, key)) { - case (_, null) { (self, false) }; - case (t, ?_) { ({ root = t; size = self.size - 1 }, true) } - } - }; - - /// Given a `map`, ordered by `compare`, deletes the entry for `key`. Returns a modified map, leaving `map` unchanged, and the - /// previous value associated with `key` or `null` if no such value exists. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map0 = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// do { - /// let (map1, prev1) = Map.take(map0, Nat.compare, 0); - /// assert Iter.toArray(Map.entries(map1)) == [(1, "One"), (2, "Two")]; - /// assert prev1 == ?"Zero"; - /// - /// let (map2, prev2) = Map.take(map0, Nat.compare, 42); - /// assert Iter.toArray(Map.entries(map2)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// assert prev2 == null; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned map shares with the `m` most of the tree nodes. - /// Garbage collecting one of maps (e.g. after an assignment `map := Map.remove(map, compare, key)`) - /// causes collecting `O(log(n))` nodes. - public func take(self : Map, compare : (implicit : (K, K) -> Order.Order), key : K) : (Map, ?V) { - switch (Internal.remove(self.root, compare, key)) { - case (t, null) { ({ root = t; size = self.size }, null) }; - case (t, v) { ({ root = t; size = self.size - 1 }, v) } - } - }; - - /// Given a `map` retrieves the key-value pair in `map` with a maximal key. If `map` is empty returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.maxEntry(map) == ?(2, "Two"); - /// assert Map.maxEntry(Map.empty()) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func maxEntry(self : Map) : ?(K, V) = Internal.maxEntry(self.root); - - /// Retrieves a key-value pair from `map` with the minimal key. If the map is empty returns `null`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Map.minEntry(map) == ?(0, "Zero"); - /// assert Map.minEntry(Map.empty()) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func minEntry(self : Map) : ?(K, V) = Internal.minEntry(self.root); - - /// Returns an Iterator (`Iter`) over the key-value pairs in the map. - /// Iterator provides a single method `next()`, which returns - /// pairs in ascending order by keys, or `null` when out of pairs to iterate over. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// var sum = 0; - /// var text = ""; - /// for ((k, v) in Map.entries(map)) { sum += k; text #= v }; - /// assert sum == 3; - /// assert text == "ZeroOneTwo" - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func entries(self : Map) : Iter.Iter<(K, V)> = Internal.iter(self.root, #fwd); - - /// Returns an Iterator (`Iter`) over the key-value pairs in the map. - /// Iterator provides a single method `next()`, which returns - /// pairs in descending order by keys, or `null` when out of pairs to iterate over. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.reverseEntries(map)) == [(2, "Two"), (1, "One"), (0, "Zero")]; - /// var sum = 0; - /// var text = ""; - /// for ((k, v) in Map.reverseEntries(map)) { sum += k; text #= v }; - /// assert sum == 3; - /// assert text == "TwoOneZero" - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func reverseEntries(self : Map) : Iter.Iter<(K, V)> = Internal.iter(self.root, #bwd); - - /// Given a `map`, returns an Iterator (`Iter`) over the keys of the `map`. - /// Iterator provides a single method `next()`, which returns - /// keys in ascending order, or `null` when out of keys to iterate over. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.keys(map)) == [0, 1, 2]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func keys(self : Map) : Iter.Iter = Iter.map(entries(self), func(kv : (K, V)) : K { kv.0 }); - - /// Given a `map`, returns an Iterator (`Iter`) over the values of the map. - /// Iterator provides a single method `next()`, which returns - /// values in ascending order of associated keys, or `null` when out of values to iterate over. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// assert Iter.toArray(Map.values(map)) == ["Zero", "One", "Two"]; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(log(n))` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func values(self : Map) : Iter.Iter = Iter.map(entries(self), func(kv : (K, V)) : V { kv.1 }); - - /// Returns a new map, containing all entries given by the iterator `i`. - /// If there are multiple entries with the same key the last one is taken. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = - /// Iter.fromArray([(0, "Zero"), (2, "Two"), (1, "One")]); - /// - /// let map = Map.fromIter(iter, Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func fromIter(iter : Iter.Iter<(K, V)>, compare : (implicit : (K, K) -> Order.Order)) : Map = Internal.fromIter(iter, compare); - - /// Convert an iterator of entries into a map. - /// If there are multiple entries with the same key the last one is taken. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = - /// Iter.fromArray([(0, "Zero"), (2, "Two"), (1, "One")]); - /// - /// let map = iter.toMap(Nat.compare); - /// - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero"), (1, "One"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func toMap(self : Iter.Iter<(K, V)>, compare : (implicit : (K, K) -> Order.Order)) : Map = Internal.fromIter(self, compare); - - /// Given a `map` and function `f`, creates a new map by applying `f` to each entry in the map `m`. Each entry - /// `(k, v)` in the old map is transformed into a new entry `(k, v2)`, where - /// the new value `v2` is created by applying `f` to `(k, v)`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func f(key : Nat, _val : Text) : Nat = key * 2; - /// - /// let resMap = Map.map(map, f); - /// - /// assert Iter.toArray(Map.entries(resMap)) == [(0, 0), (1, 2), (2, 4)]; - /// } - /// ``` - /// - /// Cost of mapping all the elements: - /// Runtime: `O(n)`. - /// Space: `O(n)` retained memory - /// where `n` denotes the number of key-value entries stored in the map. - public func map(self : Map, f : (K, V1) -> V2) : Map = Internal.map(self, f); - - /// Collapses the elements in the `map` into a single value by starting with `base` - /// and progressively combining keys and values into `base` with `combine`. Iteration runs - /// left to right. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func folder(accum : (Nat, Text), key : Nat, val : Text) : ((Nat, Text)) - /// = (key + accum.0, accum.1 # val); - /// - /// assert Map.foldLeft(map, (0, ""), folder) == (3, "ZeroOneTwo"); - /// } - /// ``` - /// - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: depends on `combine` function plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func foldLeft( - self : Map, - base : A, - combine : (A, K, V) -> A - ) : A = Internal.foldLeft(self.root, base, combine); - - /// Collapses the elements in the `map` into a single value by starting with `base` - /// and progressively combining keys and values into `base` with `combine`. Iteration runs - /// right to left. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func folder(key : Nat, val : Text, accum : (Nat, Text)) : ((Nat, Text)) - /// = (key + accum.0, accum.1 # val); - /// - /// assert Map.foldRight(map, (0, ""), folder) == (3, "TwoOneZero"); - /// } - /// ``` - /// - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: depends on `combine` function plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map. - /// - /// Note: Full map iteration creates `O(n)` temporary objects that will be collected as garbage. - public func foldRight( - self : Map, - base : A, - combine : (K, V, A) -> A - ) : A = Internal.foldRight(self.root, base, combine); - - /// Test whether all key-value pairs in `map` satisfy the given predicate `pred`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "0"), (2, "2"), (1, "1")].values(), Nat.compare); - /// - /// assert Map.all(map, func (k, v) = v == Nat.toText(k)); - /// assert not Map.all(map, func (k, v) = k < 2); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func all(self : Map, pred : (K, V) -> Bool) : Bool = Internal.all(self.root, pred); - - /// Test if any key-value pair in `map` satisfies the given predicate `pred`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "0"), (2, "2"), (1, "1")].values(), Nat.compare); - /// - /// assert Map.any(map, func (k, v) = (k >= 0)); - /// assert not Map.any(map, func (k, v) = (k >= 3)); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - /// where `n` denotes the number of key-value entries stored in the map. - public func any(self : Map, pred : (K, V) -> Bool) : Bool = Internal.any(self.root, pred); - - /// Create a new immutable key-value `map` with a single entry. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.singleton(0, "Zero"); - /// assert Iter.toArray(Map.entries(map)) == [(0, "Zero")]; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func singleton(key : K, value : V) : Map { - { - size = 1; - root = #red(#leaf, key, value, #leaf) - } - }; - - /// Apply an operation for each key-value pair contained in the map. - /// The operation is applied in ascending order of the keys. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// var sum = 0; - /// var text = ""; - /// Map.forEach(map, func (key, value) { - /// sum += key; - /// text #= value; - /// }); - /// assert sum == 3; - /// assert text == "ZeroOneTwo"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map. - public func forEach(self : Map, operation : (K, V) -> ()) = Internal.forEach(self, operation); - - /// Filter entries in a new map. - /// Returns a new map that only contains the key-value pairs - /// that fulfil the criterion function. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numberNames = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// let evenNames = Map.filter(numberNames, Nat.compare, func (key, value) { - /// key % 2 == 0 - /// }); - /// - /// assert Iter.toArray(Map.entries(evenNames)) == [(0, "Zero"), (2, "Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func filter(self : Map, compare : (implicit : (K, K) -> Order.Order), criterion : (K, V) -> Bool) : Map = Internal.filter(self, compare, criterion); - - /// Given a `map`, comparison `compare` and function `f`, - /// constructs a new map ordered by `compare`, by applying `f` to each entry in `map`. - /// For each entry `(k, v)` in the old map, if `f` evaluates to `null`, the entry is discarded. - /// Otherwise, the entry is transformed into a new entry `(k, v2)`, where - /// the new value `v2` is the result of applying `f` to `(k, v)`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// - /// func f(key : Nat, val : Text) : ?Text { - /// if(key == 0) {null} - /// else { ?("Twenty " # val)} - /// }; - /// - /// let newMap = Map.filterMap(map, Nat.compare, f); - /// - /// assert Iter.toArray(Map.entries(newMap)) == [(1, "Twenty One"), (2, "Twenty Two")]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func filterMap(self : Map, compare : (implicit : (K, K) -> Order.Order), f : (K, V1) -> ?V2) : Map = Internal.mapFilter(self, compare : (K, K) -> Order.Order, f); - - /// Validate the representation invariants of the given `map`. - /// Assert if any invariants are violated. - public func assertValid(self : Map, compare : (implicit : (K, K) -> Order.Order)) : () = Internal.validate(self, compare); - - /// Converts the `map` to its textual representation using `keyFormat` and `valueFormat` to convert each key and value to `Text`. - /// - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let map = Map.fromIter([(0, "Zero"), (2, "Two"), (1, "One")].values(), Nat.compare); - /// assert Map.toText(map, Nat.toText, func t { t }) == "PureMap{(0, Zero), (1, One), (2, Two)}"; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - /// - /// *Runtime and space assumes that `keyFormat` and `valueFormat` run in O(1) time and space. - public func toText(self : Map, keyFormat : (implicit : (toText : K -> Text)), valueFormat : (implicit : (toText : V -> Text))) : Text { - var text = "PureMap{"; - var sep = ""; - for ((k, v) in entries(self)) { - text #= sep # "(" # keyFormat(k) # ", " # valueFormat(v) # ")"; - sep := ", " - }; - text # "}" - }; - - /// Test whether two immutable maps have equal entries. - /// Assumes both maps are ordered equivalently. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let map1 = Map.fromIter([(0, "Zero"), (1, "One"), (2, "Two")].values(), Nat.compare); - /// let map2 = Map.fromIter([(2, "Two"), (1, "One"), (0, "Zero")].values(), Nat.compare); - /// assert(Map.equal(map1, map2, Nat.compare, Text.equal)); - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func equal(self : Map, other : Map, compare : (implicit : (K, K) -> Order.Order), equal : (implicit : (V, V) -> Bool)) : Bool { - if (self.size != other.size) { - return false - }; - let iterator1 = entries(self); - let iterator2 = entries(other); - loop { - let next1 = iterator1.next(); - let next2 = iterator2.next(); - switch (next1, next2) { - case (null, null) { - return true - }; - case (?(key1, value1), ?(key2, value2)) { - if (not (compare(key1, key2) == #equal) or not equal(value1, value2)) { - return false - } - }; - case _ { return false } - } - } - }; - - /// Compare two maps by primarily comparing keys and secondarily values. - /// Both maps are iterated by the ascending order of their creation and - /// order is determined by the following rules: - /// Less: - /// `map1` is less than `map2` if: - /// * the pairwise iteration hits a entry pair `entry1` and `entry2` where - /// `entry1` is less than `entry2` and all preceding entry pairs are equal, or, - /// * `map1` is a strict prefix of `map2`, i.e. `map2` has more entries than `map1` - /// and all entries of `map1` occur at the beginning of iteration `map2`. - /// `entry1` is less than `entry2` if: - /// * the key of `entry1` is less than the key of `entry2`, or - /// * `entry1` and `entry2` have equal keys and the value of `entry1` is less than - /// the value of `entry2`. - /// Equal: - /// `map1` and `map2` have same series of equal entries by pairwise iteration. - /// Greater: - /// `map1` is neither less nor equal `map2`. - /// - /// Example: - /// ```motoko - /// import Map "mo:core/pure/Map"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// - /// persistent actor { - /// let map1 = Map.fromIter([(0, "Zero"), (1, "One")].values(), Nat.compare); - /// let map2 = Map.fromIter([(0, "Zero"), (2, "Two")].values(), Nat.compare); - /// - /// assert Map.compare(map1, map2, Nat.compare, Text.compare) == #less; - /// assert Map.compare(map1, map1, Nat.compare, Text.compare) == #equal; - /// assert Map.compare(map2, map1, Nat.compare, Text.compare) == #greater - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of key-value entries stored in the map and - /// assuming that `compareKey` and `compareValue` have runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func compare(self : Map, other : Map, compareKey : (implicit : (compare : (K, K) -> Order.Order)), compareValue : (implicit : (compare : (V, V) -> Order.Order))) : Order.Order { - let iterator1 = entries(self); - let iterator2 = entries(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?(key1, value1), ?(key2, value2)) { - let keyComparison = compareKey(key1, key2); - if (keyComparison != #equal) { - return keyComparison - }; - let valueComparison = compareValue(value1, value2); - if (valueComparison != #equal) { - return valueComparison - } - } - } - } - }; - - module Internal { - - public func empty() : Map { { size = 0; root = #leaf } }; - - public func fromIter(i : Iter.Iter<(K, V)>, compare : (K, K) -> Order.Order) : Map { - var map = #leaf : Tree; - var size = 0; - for (val in i) { - map := add(map, compare, val.0, val.1); - size += 1 - }; - { root = map; size } - }; - - type List = Types.Pure.List; - - type IterRep = List<{ #tr : Tree; #xy : (K, V) }>; - - public func iter(map : Tree, direction : { #fwd; #bwd }) : Iter.Iter<(K, V)> { - let turnLeftFirst : MapTraverser = func(l, x, y, r, ts) { - ?(#tr(l), ?(#xy(x, y), ?(#tr(r), ts))) - }; - - let turnRightFirst : MapTraverser = func(l, x, y, r, ts) { - ?(#tr(r), ?(#xy(x, y), ?(#tr(l), ts))) - }; - - switch direction { - case (#fwd) IterMap(map, turnLeftFirst); - case (#bwd) IterMap(map, turnRightFirst) - } - }; - - type MapTraverser = (Tree, K, V, Tree, IterRep) -> IterRep; - - class IterMap(tree : Tree, mapTraverser : MapTraverser) { - var trees : IterRep = ?(#tr(tree), null); - public func next() : ?(K, V) { - switch (trees) { - case (null) { null }; - case (?(#tr(#leaf), ts)) { - trees := ts; - next() - }; - case (?(#xy(xy), ts)) { - trees := ts; - ?xy - }; - case (?(#tr(#red(l, x, y, r)), ts)) { - trees := mapTraverser(l, x, y, r, ts); - next() - }; - case (?(#tr(#black(l, x, y, r)), ts)) { - trees := mapTraverser(l, x, y, r, ts); - next() - } - } - } - }; - - public func map(map : Map, f : (K, V1) -> V2) : Map { - func mapRec(m : Tree) : Tree { - switch m { - case (#leaf) { #leaf }; - case (#red(l, x, y, r)) { - #red(mapRec l, x, f(x, y), mapRec r) - }; - case (#black(l, x, y, r)) { - #black(mapRec l, x, f(x, y), mapRec r) - } - } - }; - { size = map.size; root = mapRec(map.root) } - }; - - public func foldLeft( - map : Tree, - base : Accum, - combine : (Accum, Key, Value) -> Accum - ) : Accum { - switch (map) { - case (#leaf) { base }; - case (#red(l, k, v, r)) { - let left = foldLeft(l, base, combine); - let middle = combine(left, k, v); - foldLeft(r, middle, combine) - }; - case (#black(l, k, v, r)) { - let left = foldLeft(l, base, combine); - let middle = combine(left, k, v); - foldLeft(r, middle, combine) - } - } - }; - - public func foldRight( - map : Tree, - base : Accum, - combine : (Key, Value, Accum) -> Accum - ) : Accum { - switch (map) { - case (#leaf) { base }; - case (#red(l, k, v, r)) { - let right = foldRight(r, base, combine); - let middle = combine(k, v, right); - foldRight(l, middle, combine) - }; - case (#black(l, k, v, r)) { - let right = foldRight(r, base, combine); - let middle = combine(k, v, right); - foldRight(l, middle, combine) - } - } - }; - - public func forEach(map : Map, operation : (K, V) -> ()) { - func combine(_acc : Null, key : K, value : V) : Null { - operation(key, value); - null - }; - ignore foldLeft(map.root, null, combine) - }; - - public func filter(map : Map, compare : (K, K) -> Order.Order, criterion : (K, V) -> Bool) : Map { - var size = 0; - func combine(acc : Tree, key : K, value : V) : Tree { - if (criterion(key, value)) { - size += 1; - add(acc, compare, key, value) - } else acc - }; - { root = foldLeft(map.root, #leaf, combine); size } - }; - - public func mapFilter(map : Map, compare : (K, K) -> Order.Order, f : (K, V1) -> ?V2) : Map { - var size = 0; - func combine(acc : Tree, key : K, value1 : V1) : Tree { - switch (f(key, value1)) { - case null { acc }; - case (?value2) { - size += 1; - add(acc, compare, key, value2) - } - } - }; - { root = foldLeft(map.root, #leaf, combine); size } - }; - - public func get(t : Tree, compare : (K, K) -> Order.Order, x : K) : ?V { - switch t { - case (#red(l, x1, y1, r)) { - switch (compare(x, x1)) { - case (#less) { get(l, compare, x) }; - case (#equal) { ?y1 }; - case (#greater) { get(r, compare, x) } - } - }; - case (#black(l, x1, y1, r)) { - switch (compare(x, x1)) { - case (#less) { get(l, compare, x) }; - case (#equal) { ?y1 }; - case (#greater) { get(r, compare, x) } - } - }; - case (#leaf) { null } - } - }; - - public func contains(m : Tree, compare : (K, K) -> Order.Order, key : K) : Bool { - switch (get(m, compare, key)) { - case (null) { false }; - case (_) { true } - } - }; - - public func maxEntry(m : Tree) : ?(K, V) { - func rightmost(m : Tree) : (K, V) { - switch m { - case (#red(_, k, v, #leaf)) { (k, v) }; - case (#red(_, _, _, r)) { rightmost(r) }; - case (#black(_, k, v, #leaf)) { (k, v) }; - case (#black(_, _, _, r)) { rightmost(r) }; - case (#leaf) { Runtime.trap "pure/Map.maxEntry() impossible" } - } - }; - switch m { - case (#leaf) { null }; - case (_) { ?rightmost(m) } - } - }; - - public func minEntry(m : Tree) : ?(K, V) { - func leftmost(m : Tree) : (K, V) { - switch m { - case (#red(#leaf, k, v, _)) { (k, v) }; - case (#red(l, _, _, _)) { leftmost(l) }; - case (#black(#leaf, k, v, _)) { (k, v) }; - case (#black(l, _, _, _)) { leftmost(l) }; - case (#leaf) { Runtime.trap "pure/Map.minEntry() impossible" } - } - }; - switch m { - case (#leaf) { null }; - case (_) { ?leftmost(m) } - } - }; - - public func all(m : Tree, pred : (K, V) -> Bool) : Bool { - switch m { - case (#red(l, k, v, r)) { - pred(k, v) and all(l, pred) and all(r, pred) - }; - case (#black(l, k, v, r)) { - pred(k, v) and all(l, pred) and all(r, pred) - }; - case (#leaf) { true } - } - }; - - public func any(m : Tree, pred : (K, V) -> Bool) : Bool { - switch m { - case (#red(l, k, v, r)) { - pred(k, v) or any(l, pred) or any(r, pred) - }; - case (#black(l, k, v, r)) { - pred(k, v) or any(l, pred) or any(r, pred) - }; - case (#leaf) { false } - } - }; - - func redden(t : Tree) : Tree { - switch t { - case (#black(l, x, y, r)) { (#red(l, x, y, r)) }; - case _ { - Runtime.trap "pure/Map.redden() impossible" - } - } - }; - - func lbalance(left : Tree, x : K, y : V, right : Tree) : Tree { - switch (left, right) { - case (#red(#red(l1, x1, y1, r1), x2, y2, r2), r) { - #red( - #black(l1, x1, y1, r1), - x2, - y2, - #black(r2, x, y, r) - ) - }; - case (#red(l1, x1, y1, #red(l2, x2, y2, r2)), r) { - #red( - #black(l1, x1, y1, l2), - x2, - y2, - #black(r2, x, y, r) - ) - }; - case _ { - #black(left, x, y, right) - } - } - }; - - func rbalance(left : Tree, x : K, y : V, right : Tree) : Tree { - switch (left, right) { - case (l, #red(l1, x1, y1, #red(l2, x2, y2, r2))) { - #red( - #black(l, x, y, l1), - x1, - y1, - #black(l2, x2, y2, r2) - ) - }; - case (l, #red(#red(l1, x1, y1, r1), x2, y2, r2)) { - #red( - #black(l, x, y, l1), - x1, - y1, - #black(r1, x2, y2, r2) - ) - }; - case _ { - #black(left, x, y, right) - } - } - }; - - type ClashResolver = { old : A; new : A } -> A; - - func insertWith( - m : Tree, - compare : (K, K) -> Order.Order, - key : K, - val : V, - onClash : ClashResolver - ) : Tree { - func ins(tree : Tree) : Tree { - switch tree { - case (#black(left, x, y, right)) { - switch (compare(key, x)) { - case (#less) { - lbalance(ins left, x, y, right) - }; - case (#greater) { - rbalance(left, x, y, ins right) - }; - case (#equal) { - let newVal = onClash({ new = val; old = y }); - #black(left, key, newVal, right) - } - } - }; - case (#red(left, x, y, right)) { - switch (compare(key, x)) { - case (#less) { - #red(ins left, x, y, right) - }; - case (#greater) { - #red(left, x, y, ins right) - }; - case (#equal) { - let newVal = onClash { new = val; old = y }; - #red(left, key, newVal, right) - } - } - }; - case (#leaf) { - #red(#leaf, key, val, #leaf) - } - } - }; - switch (ins m) { - case (#red(left, x, y, right)) { - #black(left, x, y, right) - }; - case other { other } - } - }; - - public func swap( - m : Tree, - compare : (K, K) -> Order.Order, - key : K, - val : V - ) : (Tree, ?V) { - var oldVal : ?V = null; - func onClash(clash : { old : V; new : V }) : V { - oldVal := ?clash.old; - clash.new - }; - let res = insertWith(m, compare, key, val, onClash); - (res, oldVal) - }; - - public func add( - m : Tree, - compare : (K, K) -> Order.Order, - key : K, - val : V - ) : Tree = swap(m, compare, key, val).0; - - func balLeft(left : Tree, x : K, y : V, right : Tree) : Tree { - switch (left, right) { - case (#red(l1, x1, y1, r1), r) { - #red( - #black(l1, x1, y1, r1), - x, - y, - r - ) - }; - case (_, #black(l2, x2, y2, r2)) { - rbalance(left, x, y, #red(l2, x2, y2, r2)) - }; - case (_, #red(#black(l2, x2, y2, r2), x3, y3, r3)) { - #red( - #black(left, x, y, l2), - x2, - y2, - rbalance(r2, x3, y3, redden r3) - ) - }; - case _ { Runtime.trap "pure/Map.balLeft() impossible" } - } - }; - - func balRight(left : Tree, x : K, y : V, right : Tree) : Tree { - switch (left, right) { - case (l, #red(l1, x1, y1, r1)) { - #red( - l, - x, - y, - #black(l1, x1, y1, r1) - ) - }; - case (#black(l1, x1, y1, r1), r) { - lbalance(#red(l1, x1, y1, r1), x, y, r) - }; - case (#red(l1, x1, y1, #black(l2, x2, y2, r2)), r3) { - #red( - lbalance(redden l1, x1, y1, l2), - x2, - y2, - #black(r2, x, y, r3) - ) - }; - case _ { Runtime.trap "pure/Map.balRight() impossible" } - } - }; - - func append(left : Tree, right : Tree) : Tree { - switch (left, right) { - case (#leaf, _) { right }; - case (_, #leaf) { left }; - case ( - #red(l1, x1, y1, r1), - #red(l2, x2, y2, r2) - ) { - switch (append(r1, l2)) { - case (#red(l3, x3, y3, r3)) { - #red( - #red(l1, x1, y1, l3), - x3, - y3, - #red(r3, x2, y2, r2) - ) - }; - case r1l2 { - #red(l1, x1, y1, #red(r1l2, x2, y2, r2)) - } - } - }; - case (t1, #red(l2, x2, y2, r2)) { - #red(append(t1, l2), x2, y2, r2) - }; - case (#red(l1, x1, y1, r1), t2) { - #red(l1, x1, y1, append(r1, t2)) - }; - case (#black(l1, x1, y1, r1), #black(l2, x2, y2, r2)) { - switch (append(r1, l2)) { - case (#red(l3, x3, y3, r3)) { - #red( - #black(l1, x1, y1, l3), - x3, - y3, - #black(r3, x2, y2, r2) - ) - }; - case r1l2 { - balLeft( - l1, - x1, - y1, - #black(r1l2, x2, y2, r2) - ) - } - } - } - } - }; - - public func delete(m : Tree, compare : (K, K) -> Order.Order, key : K) : Tree = remove(m, compare, key).0; - - public func remove(tree : Tree, compare : (K, K) -> Order.Order, x : K) : (Tree, ?V) { - var y0 : ?V = null; - func delNode(left : Tree, x1 : K, y1 : V, right : Tree) : Tree { - switch (compare(x, x1)) { - case (#less) { - let newLeft = del left; - switch left { - case (#black(_, _, _, _)) { - balLeft(newLeft, x1, y1, right) - }; - case _ { - #red(newLeft, x1, y1, right) - } - } - }; - case (#greater) { - let newRight = del right; - switch right { - case (#black(_, _, _, _)) { - balRight(left, x1, y1, newRight) - }; - case _ { - #red(left, x1, y1, newRight) - } - } - }; - case (#equal) { - y0 := ?y1; - append(left, right) - } - } - }; - func del(tree : Tree) : Tree { - switch tree { - case (#red(left, x, y, right)) { - delNode(left, x, y, right) - }; - case (#black(left, x, y, right)) { - delNode(left, x, y, right) - }; - case (#leaf) { - tree - } - } - }; - switch (del(tree)) { - case (#red(left, x, y, right)) { (#black(left, x, y, right), y0) }; - case other { (other, y0) } - } - }; - - // Test helper - public func validate(rbMap : Map, comp : (K, K) -> Order.Order) { - ignore blackDepth(rbMap.root, comp) - }; - - func blackDepth(node : Tree, comp : (K, K) -> Order.Order) : Nat { - func checkNode(left : Tree, key : K, right : Tree) : Nat { - checkKey(left, func(x : K) : Bool { comp(x, key) == #less }); - checkKey(right, func(x : K) : Bool { comp(x, key) == #greater }); - let leftBlacks = blackDepth(left, comp); - let rightBlacks = blackDepth(right, comp); - assert (leftBlacks == rightBlacks); - leftBlacks - }; - switch node { - case (#leaf) 0; - case (#red(left, key, _, right)) { - let leftBlacks = checkNode(left, key, right); - assert (not isRed(left)); - assert (not isRed(right)); - leftBlacks - }; - case (#black(left, key, _, right)) { - checkNode(left, key, right) + 1 - } - } - }; - - func isRed(node : Tree) : Bool { - switch node { - case (#red(_, _, _, _)) true; - case _ false - } - }; - - func checkKey(node : Tree, isValid : K -> Bool) { - switch node { - case (#leaf) {}; - case (#red(_, key, _, _)) { - assert (isValid(key)) - }; - case (#black(_, key, _, _)) { - assert (isValid(key)) - } - } - } - }; - -} diff --git a/.mops/core@2.5.0/src/pure/Queue.mo b/.mops/core@2.5.0/src/pure/Queue.mo deleted file mode 100644 index e179de0..0000000 --- a/.mops/core@2.5.0/src/pure/Queue.mo +++ /dev/null @@ -1,659 +0,0 @@ -/// Double-ended queue of a generic element type `T`. -/// -/// The interface is purely functional, not imperative, and queues are immutable values. -/// In particular, Queue operations such as push and pop do not update their input queue but, instead, return the -/// value of the modified Queue, alongside any other data. -/// The input queue is left unchanged. -/// -/// Examples of use-cases: -/// Queue (FIFO) by using `pushBack()` and `popFront()`. -/// Stack (LIFO) by using `pushFront()` and `popFront()`. -/// -/// A Queue is internally implemented as two lists, a head access list and a (reversed) tail access list, -/// that are dynamically size-balanced by splitting. -/// -/// Construction: Create a new queue with the `empty()` function. -/// -/// Note on the costs of push and pop functions: -/// * Runtime: `O(1)` amortized costs, `O(size)` worst case cost per single call. -/// * Space: `O(1)` amortized costs, `O(size)` worst case cost per single call. -/// -/// `n` denotes the number of elements stored in the queue. -/// -/// Note that some operations that traverse the elements of the queue (e.g. `forEach`, `values`) preserve the order of the elements, -/// whereas others (e.g. `map`, `contains`) do NOT guarantee that the elements are visited in any order. -/// The order is undefined to avoid allocations, making these operations more efficient. -/// -/// ```motoko name=import -/// import Queue "mo:core/pure/Queue"; -/// ``` - -import Iter "../Iter"; -import List "List"; -import Order "../Order"; -import Types "../Types"; -import Array "../Array"; -import Prim "mo:⛔"; - -module { - /// @deprecated M0235 - type List = Types.Pure.List; - - /// Double-ended queue data type. - public type Queue = Types.Pure.Queue; - - /// Create a new empty queue. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func empty() : Queue = (null, 0, null); - - /// Determine whether a queue is empty. - /// Returns true if `queue` is empty, otherwise `false`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func isEmpty(self : Queue) : Bool = self.1 == 0; - - /// Create a new queue comprising a single element. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.singleton(25); - /// assert Queue.size(queue) == 1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func singleton(item : T) : Queue = (null, 1, ?(item, null)); - - /// Determine the number of elements contained in a queue. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.singleton(42); - /// assert Queue.size(queue) == 1; - /// } - /// ``` - /// - /// Runtime: `O(1)` in Release profile (compiled with `--release` flag), `O(size)` otherwise. - /// - /// Space: `O(1)`. - public func size(self : Queue) : Nat { - debug assert self.1 == List.size(self.0) + List.size(self.2); - self.1 - }; - - /// Check if a queue contains a specific element. - /// Returns true if the queue contains an element equal to `item` according to the `equal` function. - /// - /// Note: The order in which elements are visited is undefined, for performance reasons. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.contains(queue, Nat.equal, 2); - /// assert not Queue.contains(queue, Nat.equal, 4); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func contains(self : Queue, equal : (implicit : (T, T) -> Bool), item : T) : Bool = List.contains(self.0, equal, item) or List.contains(self.2, equal, item); - - /// Inspect the optional element on the front end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, the front element of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekFront(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func peekFront(self : Queue) : ?T = switch self { - case ((?(x, _), _, _) or (_, _, ?(x, null))) ?x; - case _ { debug assert List.isEmpty(self.2); null } - }; - - /// Inspect the optional element on the back end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, the back element of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// assert Queue.peekBack(queue) == ?2; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func peekBack(self : Queue) : ?T = switch self { - case ((_, _, ?(x, _)) or (?(x, null), _, _)) ?x; - case _ { debug assert List.isEmpty(self.0); null } - }; - - // helper to rebalance the queue after getting lopsided - func check(q : Queue) : Queue { - switch q { - case (null, n, r) { - let (a, b) = List.split(r, n / 2); - (List.reverse b, n, a) - }; - case (f, n, null) { - let (a, b) = List.split(f, n / 2); - (a, n, List.reverse b) - }; - case q q - } - }; - - /// Insert a new element on the front end of a queue. - /// Returns the new queue with `element` in the front followed by the elements of `queue`. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekFront(queue) == ?1; - /// assert Queue.peekBack(queue) == ?2; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: `O(size)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(size)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the queue. - public func pushFront(self : Queue, element : T) : Queue = check(?(element, self.0), self.1 + 1, self.2); - - /// Insert a new element on the back end of a queue. - /// Returns the new queue with all the elements of `queue`, followed by `element` on the back. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// assert Queue.peekBack(queue) == ?2; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: `O(size)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(size)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the queue. - public func pushBack(self : Queue, element : T) : Queue = check(self.0, self.1 + 1, ?(element, self.2)); - - /// Remove the element on the front end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, it returns a pair of - /// the first element and a new queue that contains all the remaining elements of `queue`. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko include=import - /// import Runtime "mo:core/Runtime"; - /// - /// persistent actor { - /// let initial = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// // initial queue with elements [1, 2] - /// switch (Queue.popFront(initial)) { - /// case null Runtime.trap "Empty queue impossible"; - /// case (?(frontElement, remainingQueue)) { - /// assert frontElement == 1; - /// assert Queue.size(remainingQueue) == 1 - /// } - /// } - /// } - /// ``` - /// - /// Runtime: `O(size)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(size)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the queue. - public func popFront(self : Queue) : ?(T, Queue) = if (self.1 == 0) null else switch self { - case (?(i, f), n, b) ?(i, (f, n - 1, b)); - case (null, _, ?(i, null)) ?(i, (null, 0, null)); - case _ popFront(check self) - }; - - /// Remove the element on the back end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, it returns a pair of - /// a new queue that contains the remaining elements of `queue` - /// and, as the second pair item, the removed back element. - /// - /// This may involve dynamic rebalancing of the two, internally used lists. - /// - /// Example: - /// ```motoko include=import - /// import Runtime "mo:core/Runtime"; - /// - /// persistent actor { - /// let initial = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// // initial queue with elements [1, 2] - /// let reduced = Queue.popBack(initial); - /// switch reduced { - /// case null Runtime.trap("Empty queue impossible"); - /// case (?result) { - /// let reducedQueue = result.0; - /// let removedElement = result.1; - /// assert removedElement == 2; - /// assert Queue.size(reducedQueue) == 1; - /// } - /// } - /// } - /// ``` - /// - /// Runtime: `O(size)` worst-case, amortized to `O(1)`. - /// - /// Space: `O(size)` worst-case, amortized to `O(1)`. - /// - /// `n` denotes the number of elements stored in the queue. - public func popBack(self : Queue) : ?(Queue, T) = if (self.1 == 0) null else switch self { - case (f, n, ?(i, b)) ?((f, n - 1, b), i); - case (?(i, null), _, null) ?((null, 0, null), i); - case _ popBack(check self) - }; - - /// Turn an iterator into a queue, consuming it. - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([0, 1, 2, 3, 4].values()); - /// assert Queue.size(queue) == 5; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromIter(iter : Iter.Iter) : Queue { - let list = List.fromIter iter; - check(list, List.size list, null) - }; - - /// Convert an iterator to a queue, consuming it. - /// Example: - /// ```motoko include=import - /// persistent actor { - /// transient let iter = [0, 1, 2, 3, 4].values(); - /// - /// let queue = iter.toQueue(); - /// assert Queue.size(queue) == 5; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toQueue(self : Iter.Iter) : Queue { - fromIter(self) - }; - - /// Create a queue from an array. - /// Elements appear in the same order as in the array. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromArray(["A", "B", "C"]); - /// assert Queue.size(queue) == 3; - /// assert Queue.peekFront(queue) == ?"A"; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func fromArray(array : [T]) : Queue { - let list = List.fromArray array; - check(list, array.size(), null) - }; - - /// Create an immutable array from a queue. - /// Elements appear in the same order as in the queue (front to back). - /// - /// Example: - /// ```motoko include=import - /// import Array "mo:core/Array"; - /// - /// persistent actor { - /// let queue = Queue.fromArray(["A", "B", "C"]); - /// let array = Queue.toArray(queue); - /// assert array == ["A", "B", "C"]; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func toArray(self : Queue) : [T] { - let iter = values(self); - Array.tabulate( - self.1, - func(i) { - switch (iter.next()) { - case null { - Prim.trap("pure/Queue.toArray: unexpected end of iterator") - }; - case (?value) { value } - } - } - ) - }; - - /// Convert a queue to an iterator of its elements in front-to-back order. - /// - /// Performance note: Creating the iterator needs `O(size)` runtime and space! - /// - /// Example: - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Iter.toArray(Queue.values(queue)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func values(self : Queue) : Iter.Iter = Iter.concat(List.values(self.0), List.values(List.reverse(self.2))); - - /// Compare two queues for equality using the provided equality function. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2].values()); - /// let queue2 = Queue.fromIter([1, 2].values()); - /// let queue3 = Queue.fromIter([1, 3].values()); - /// assert Queue.equal(queue1, queue2, Nat.equal); - /// assert not Queue.equal(queue1, queue3, Nat.equal); - /// } - /// ``` - /// - /// Runtime: O(size) - /// - /// Space: O(size) - public func equal(self : Queue, other : Queue, equal : (implicit : (T, T) -> Bool)) : Bool { - if (self.1 != other.1) { - return false - }; - let (iter1, iter2) = (values(self), values(other)); - loop { - switch (iter1.next(), iter2.next()) { - case (null, null) { return true }; - case (?v1, ?v2) { - if (not equal(v1, v2)) { return false } - }; - case (_, _) { return false } - } - } - }; - - /// Return true if the given predicate `f` is true for all queue - /// elements. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let allGreaterThanOne = Queue.all(queue, func n = n > 1); - /// assert not allGreaterThanOne; // false because 1 is not > 1 - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` as the current implementation uses `values` to iterate over the queue. - /// - /// *Runtime and space assumes that the `predicate` runs in `O(1)` time and space. - public func all(self : Queue, predicate : T -> Bool) : Bool { - for (item in values self) if (not (predicate item)) return false; - return true - }; - - /// Return true if there exists a queue element for which - /// the given predicate `f` is true. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let hasGreaterThanOne = Queue.any(queue, func n = n > 1); - /// assert hasGreaterThanOne; // true because 2 and 3 are > 1 - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` as the current implementation uses `values` to iterate over the queue. - /// - /// *Runtime and space assumes that the `predicate` runs in `O(1)` time and space. - public func any(self : Queue, predicate : T -> Bool) : Bool { - for (item in values self) if (predicate item) return true; - return false - }; - - /// Call the given function for its side effect, with each queue element in turn. - /// The order of visiting elements is front-to-back. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// var text = ""; - /// let queue = Queue.fromIter(["A", "B", "C"].values()); - /// Queue.forEach(queue, func n = text #= n); - /// assert text == "ABC"; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func forEach(self : Queue, f : T -> ()) = for (item in values self) f item; - - /// Call the given function `f` on each queue element and collect the results - /// in a new queue. - /// - /// Note: The order of visiting elements is undefined with the current implementation. - /// - /// Example: - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([0, 1, 2].values()); - /// let textQueue = Queue.map(queue, Nat.toText); - /// assert Iter.toArray(Queue.values(textQueue)) == ["0", "1", "2"]; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func map(self : Queue, f : T1 -> T2) : Queue { - let (fr, n, b) = self; - (List.map(fr, f), n, List.map(b, f)) - }; - - /// Create a new queue with only those elements of the original queue for which - /// the given function (often called the _predicate_) returns true. - /// - /// Note: The order of visiting elements is undefined with the current implementation. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([0, 1, 2, 1].values()); - /// let filtered = Queue.filter(queue, func n = n != 1); - /// assert Queue.size(filtered) == 2; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func filter(self : Queue, predicate : T -> Bool) : Queue { - let (fr, _, b) = self; - let front = List.filter(fr, predicate); - let back = List.filter(b, predicate); - check(front, List.size front + List.size back, back) - }; - - /// Call the given function on each queue element, and collect the non-null results - /// in a new queue. - /// - /// Note: The order of visiting elements is undefined with the current implementation. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let doubled = Queue.filterMap( - /// queue, - /// func n = if (n > 1) ?(n * 2) else null - /// ); - /// assert Queue.size(doubled) == 2; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func filterMap(self : Queue, f : T -> ?U) : Queue { - let (fr, _n, b) = self; - let front = List.filterMap(fr, f); - let back = List.filterMap(b, f); - check(front, List.size front + List.size back, back) - }; - - /// Convert a queue to its text representation using the provided conversion function. - /// This function is meant to be used for debugging and testing purposes. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.toText(queue, Nat.toText) == "PureQueue[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func toText(self : Queue, f : (implicit : (toText : T -> Text))) : Text { - var text = "PureQueue["; - func add(item : T) { - if (text.size() > 10) text #= ", "; - text #= f(item) - }; - List.forEach(self.0, add); - List.forEach(List.reverse(self.2), add); - text # "]" - }; - - /// Compare two queues using lexicographic ordering specified by argument function `compareItem`. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2].values()); - /// let queue2 = Queue.fromIter([1, 3].values()); - /// assert Queue.compare(queue1, queue2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that argument `compareItem` runs in `O(1)` time and space. - public func compare(self : Queue, other : Queue, compareItem : (implicit : (compare : (T, T) -> Order.Order))) : Order.Order { - let (i1, i2) = (values self, values other); - loop switch (i1.next(), i2.next()) { - case (?v1, ?v2) switch (compareItem(v1, v2)) { - case (#equal) (); - case c return c - }; - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater - } - }; - - /// Reverse the order of elements in a queue. - /// This operation is cheap, it does NOT require copying the elements. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let reversed = Queue.reverse(queue); - /// assert Queue.peekFront(reversed) == ?3; - /// assert Queue.peekBack(reversed) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func reverse(self : Queue) : Queue = (self.2, self.1, self.0) -} diff --git a/.mops/core@2.5.0/src/pure/RealTimeQueue.mo b/.mops/core@2.5.0/src/pure/RealTimeQueue.mo deleted file mode 100644 index adeb25f..0000000 --- a/.mops/core@2.5.0/src/pure/RealTimeQueue.mo +++ /dev/null @@ -1,1175 +0,0 @@ -/// Double-ended immutable queue with guaranteed `O(1)` push/pop operations (caveat: high constant factor). -/// For a default immutable queue implementation, see `pure/Queue`. -/// -/// This module provides an alternative implementation with better worst-case performance for single operations, e.g. `pushBack` and `popFront`. -/// These operations are always constant time, `O(1)`, which eliminates spikes in performance of `pure/Queue` operations -/// that are caused by the amortized nature of the `pure/Queue` implementation, which can lead to `O(n)` worst-case performance for a single operation. -/// The spikes in performance can cause a single message to take multiple more rounds to complete than most other messages. -/// -/// However, the `O(1)` operations come at a cost of higher constant factor than the `pure/Queue` implementation: -/// - 'pop' operations are on average 3x more expensive -/// - 'push' operations are on average 8x more expensive -/// -/// For better performance across multiple operations and when the spikes in single operations are not a problem, use `pure/Queue`. -/// For guaranteed `O(1)` operations, use `pure/RealTimeQueue`. -/// -/// --- -/// -/// The interface is purely functional, not imperative, and queues are immutable values. -/// In particular, Queue operations such as push and pop do not update their input queue but, instead, return the -/// value of the modified Queue, alongside any other data. -/// The input queue is left unchanged. -/// -/// Examples of use-cases: -/// - Queue (FIFO) by using `pushBack()` and `popFront()`. -/// - Stack (LIFO) by using `pushFront()` and `popFront()`. -/// - Deque (double-ended queue) by using any combination of push/pop operations on either end. -/// -/// A Queue is internally implemented as a real-time double-ended queue based on the paper -/// "Real-Time Double-Ended Queue Verified (Proof Pearl)". The implementation maintains -/// worst-case constant time `O(1)` for push/pop operations through gradual rebalancing steps. -/// -/// Construction: Create a new queue with the `empty()` function. -/// -/// Note that some operations that traverse the elements of the queue (e.g. `forEach`, `values`) preserve the order of the elements, -/// whereas others (e.g. `map`, `contains`) do NOT guarantee that the elements are visited in any order. -/// The order is undefined to avoid allocations, making these operations more efficient. -/// -/// ```motoko name=import -/// import Queue "mo:core/pure/RealTimeQueue"; -/// ``` - -import Types "../Types"; -import List "List"; -import Option "../Option"; -import { trap } "../Runtime"; -import Iter "../Iter"; - -module { - /// The real-time queue data structure can be in one of the following states: - /// - /// - `#empty`: the queue is empty - /// - `#one`: the queue contains a single element - /// - `#two`: the queue contains two elements - /// - `#three`: the queue contains three elements - /// - `#idles`: the queue is in the idle state, where `l` and `r` are non-empty stacks of elements fulfilling the size invariant - /// - `#rebal`: the queue is in the rebalancing state - public type Queue = { - #empty; - #one : T; - #two : (T, T); - #three : (T, T, T); - #idles : (Idle, Idle); - #rebal : States - }; - - /// Create a new empty queue. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func empty() : Queue = #empty; - - /// Determine whether a queue is empty. - /// Returns true if `queue` is empty, otherwise `false`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.empty(); - /// assert Queue.isEmpty(queue); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func isEmpty(self : Queue) : Bool = switch self { - case (#empty) true; - case _ false - }; - - /// Create a new queue comprising a single element. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.singleton(25); - /// assert Queue.size(queue) == 1; - /// assert Queue.peekFront(queue) == ?25; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func singleton(element : T) : Queue = #one(element); - - /// Determine the number of elements contained in a queue. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.singleton(42); - /// assert Queue.size(queue) == 1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func size(self : Queue) : Nat = switch self { - case (#empty) 0; - case (#one _) 1; - case (#two _) 2; - case (#three _) 3; - case (#idles((l, nL), (r, nR))) { - debug assert Stacks.size(l) == nL and Stacks.size(r) == nR; - nL + nR - }; - case (#rebal(_, big, small)) BigState.size(big) + SmallState.size(small) - }; - - /// Test if a queue contains a given value. - /// Returns true if the queue contains the item, otherwise false. - /// - /// Note: The order in which elements are visited is undefined, for performance reasons. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// assert Queue.contains(queue, Nat.equal, 1); - /// assert not Queue.contains(queue, Nat.equal, 3); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(1)` - public func contains(self : Queue, equal : (implicit : (T, T) -> Bool), item : T) : Bool = switch self { - case (#empty) false; - case (#one(x)) equal(x, item); - case (#two(x, y)) equal(x, item) or equal(y, item); - case (#three(x, y, z)) equal(x, item) or equal(y, item) or equal(z, item); - case (#idles(((l1, l2), _), ((r1, r2), _))) List.contains(l1, equal, item) or List.contains(l2, equal, item) or List.contains(r2, equal, item) or List.contains(r1, equal, item); // note that the order of the right stack is reversed, but for this operation it does not matter - case (#rebal(_, big, small)) { - let (extraB, _, (oldB1, oldB2), _) = BigState.current(big); - let (extraS, _, (oldS1, oldS2), _) = SmallState.current(small); - // note that the order of one of the stacks is reversed (depending on the `direction` field), but for this operation it does not matter - List.contains(extraB, equal, item) or List.contains(oldB1, equal, item) or List.contains(oldB2, equal, item) or List.contains(extraS, equal, item) or List.contains(oldS1, equal, item) or List.contains(oldS2, equal, item) - } - }; - - /// Inspect the optional element on the front end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, the front element of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekFront(queue) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func peekFront(self : Queue) : ?T = switch self { - case (#idles((l, _), _)) Stacks.first(l); - case (#rebal(dir, big, small)) switch dir { - case (#left) ?SmallState.peek(small); - case (#right) ?BigState.peek(big) - }; - case (#empty) null; - case (#one(x)) ?x; - case (#two(x, _)) ?x; - case (#three(x, _, _)) ?x - }; - - /// Inspect the optional element on the back end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, the back element of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekBack(queue) == ?2; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// - /// Space: `O(1)`. - public func peekBack(self : Queue) : ?T = switch self { - case (#idles(_, (r, _))) Stacks.first(r); - case (#rebal(dir, big, small)) switch dir { - case (#left) ?BigState.peek(big); - case (#right) ?SmallState.peek(small) - }; - case (#empty) null; - case (#one(x)) ?x; - case (#two(_, y)) ?y; - case (#three(_, _, z)) ?z - }; - - /// Insert a new element on the front end of a queue. - /// Returns the new queue with `element` in the front followed by the elements of `queue`. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushFront(Queue.pushFront(Queue.empty(), 2), 1); - /// assert Queue.peekFront(queue) == ?1; - /// assert Queue.peekBack(queue) == ?2; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: `O(1)` worst-case! - /// - /// Space: `O(1)` worst-case! - public func pushFront(self : Queue, element : T) : Queue = switch self { - case (#idles(l0, rnR)) { - let lnL = Idle.push(l0, element); // enque the element to the left end - // check if the size invariant still holds - if (3 * rnR.1 >= lnL.1) { - debug assert 3 * lnL.1 >= rnR.1; - #idles(lnL, rnR) - } else { - // initiate the rebalancing process - let (l, nL) = lnL; - let (r, nR) = rnR; - let targetSizeL = nL - nR - 1 : Nat; - let targetSizeR = 2 * nR + 1; - debug assert targetSizeL + targetSizeR == nL + nR; - let big = #big1(Current.new(l, targetSizeL), l, null, targetSizeL); - let small = #small1(Current.new(r, targetSizeR), r, null); - let states = (#right, big, small); - let states6 = States.step(States.step(States.step(States.step(States.step(States.step(states)))))); - #rebal(states6) - } - }; - // if the queue is in the middle of a rebalancing process: push the element and advance the rebalancing process by 4 steps - // move back into the idle state if the rebalancing is done - case (#rebal(dir, big0, small0)) switch dir { - case (#right) { - let big = BigState.push(big0, element); - let states4 = States.step(States.step(States.step(States.step((#right, big, small0))))); - debug assert states4.0 == #right; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(big, small); - #idles(big, small) - }; - case _ #rebal(states4) - } - }; - case (#left) { - let small = SmallState.push(small0, element); - let states4 = States.step(States.step(States.step(States.step((#left, big0, small))))); - debug assert states4.0 == #left; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(small, big); - #idles(small, big) // swapped because dir=left - }; - case _ #rebal(states4) - } - } - }; - case (#empty) #one(element); - case (#one(y)) #two(element, y); - case (#two(y, z)) #three(element, y, z); - case (#three(a, b, c)) { - let i1 = ((?(element, ?(a, null)), null), 2); - let i2 = ((?(c, ?(b, null)), null), 2); - #idles(i1, i2) - } - }; - - /// Insert a new element on the back end of a queue. - /// Returns the new queue with all the elements of `queue`, followed by `element` on the back. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// assert Queue.peekBack(queue) == ?2; - /// assert Queue.size(queue) == 2; - /// } - /// ``` - /// - /// Runtime: `O(1)` worst-case! - /// - /// Space: `O(1)` worst-case! - public func pushBack(self : Queue, element : T) : Queue = switch self { - // Equivalent to: `reverse(pushFront(reverse(queue), element))`. Inlined for performance. - case (#idles(rnR, l0)) { - // ^ reversed input - let lnL = Idle.push(l0, element); - if (3 * rnR.1 >= lnL.1) { - debug assert 3 * lnL.1 >= rnR.1; - #idles(rnR, lnL) // reversed output - } else { - let (l, nL) = lnL; - let (r, nR) = rnR; - let targetSizeL = nL - nR - 1 : Nat; - let targetSizeR = 2 * nR + 1; - debug assert targetSizeL + targetSizeR == nL + nR; - let big = #big1(Current.new(l, targetSizeL), l, null, targetSizeL); - let small = #small1(Current.new(r, targetSizeR), r, null); - let states = (#left, big, small); // reversed output - let states6 = States.step(States.step(States.step(States.step(States.step(States.step(states)))))); - #rebal(states6) - } - }; - case (#rebal(dir, big0, small0)) switch dir { - case (#left) { - // ^ reversed input - let big = BigState.push(big0, element); - let states4 = States.step(States.step(States.step(States.step((#left, big, small0))))); // reversed output - debug assert states4.0 == #left; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(big, small); - #idles(small, big) // reversed output - }; - case _ #rebal(states4) - } - }; - case (#right) { - // ^ reversed input - let small = SmallState.push(small0, element); - let states4 = States.step(States.step(States.step(States.step((#right, big0, small))))); // reversed output - debug assert states4.0 == #right; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(small, big); - #idles(big, small) // reversed output - }; - case _ #rebal(states4) - } - } - }; - case (#empty) #one(element); - case (#one(y)) #two(y, element); - case (#two(y, z)) #three(y, z, element); - case (#three(a, b, c)) { - let i1 = ((?(a, ?(b, null)), null), 2); - let i2 = ((?(element, ?(c, null)), null), 2); - #idles(i1, i2) - } - }; - - /// Remove the element on the front end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, it returns a pair of - /// the first element and a new queue that contains all the remaining elements of `queue`. - /// - /// Example: - /// ```motoko include=import - /// import Runtime "mo:core/Runtime"; - /// - /// persistent actor { - /// do { - /// let initial = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// let ?(frontElement, remainingQueue) = Queue.popFront(initial) else Runtime.trap "Empty queue impossible"; - /// assert frontElement == 1; - /// assert Queue.size(remainingQueue) == 1; - /// } - /// } - /// ``` - /// - /// Runtime: `O(1)` worst-case! - /// - /// Space: `O(1)` worst-case! - public func popFront(self : Queue) : ?(T, Queue) = switch self { - case (#idles(l0, rnR)) { - let (x, lnL) = Idle.pop(l0); - if (3 * lnL.1 >= rnR.1) { - ?(x, #idles(lnL, rnR)) - } else if (lnL.1 >= 1) { - let (l, nL) = lnL; - let (r, nR) = rnR; - let targetSizeL = 2 * nL + 1; - let targetSizeR = nR - nL - 1 : Nat; - debug assert targetSizeL + targetSizeR == nL + nR; - let small = #small1(Current.new(l, targetSizeL), l, null); - let big = #big1(Current.new(r, targetSizeR), r, null, targetSizeR); - let states = (#left, big, small); - let states6 = States.step(States.step(States.step(States.step(States.step(States.step(states)))))); - ?(x, #rebal(states6)) - } else { - ?(x, Stacks.smallqueue(rnR.0)) - } - }; - case (#rebal(dir, big0, small0)) switch dir { - case (#left) { - let (x, small) = SmallState.pop(small0); - let states4 = States.step(States.step(States.step(States.step((#left, big0, small))))); - debug assert states4.0 == #left; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(small, big); - ?(x, #idles(small, big)) - }; - case _ ?(x, #rebal(states4)) - } - }; - case (#right) { - let (x, big) = BigState.pop(big0); - let states4 = States.step(States.step(States.step(States.step((#right, big, small0))))); - debug assert states4.0 == #right; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(big, small); - ?(x, #idles(big, small)) - }; - case _ ?(x, #rebal(states4)) - } - } - }; - case (#empty) null; - case (#one(x)) ?(x, #empty); - case (#two(x, y)) ?(x, #one(y)); - case (#three(x, y, z)) ?(x, #two(y, z)) - }; - - /// Remove the element on the back end of a queue. - /// Returns `null` if `queue` is empty. Otherwise, it returns a pair of - /// a new queue that contains the remaining elements of `queue` - /// and, as the second pair item, the removed back element. - /// - /// Example: - /// ```motoko include=import - /// import Runtime "mo:core/Runtime"; - /// - /// persistent actor { - /// do { - /// let initial = Queue.pushBack(Queue.pushBack(Queue.empty(), 1), 2); - /// let ?(reducedQueue, removedElement) = Queue.popBack(initial) else Runtime.trap "Empty queue impossible"; - /// assert removedElement == 2; - /// assert Queue.size(reducedQueue) == 1; - /// } - /// } - /// ``` - /// - /// Runtime: `O(1)` worst-case! - /// - /// Space: `O(1)` worst-case! - public func popBack(self : Queue) : ?(Queue, T) = switch self { - // Equivalent to: - // = do ? { let (x, queue2) = popFront(reverse(queue))!; (reverse(queue2), x) }; - // Inlined for performance. - case (#idles(rnR, l0)) { - // ^ reversed input - let (x, lnL) = Idle.pop(l0); - if (3 * lnL.1 >= rnR.1) { - ?(#idles(rnR, lnL), x) // reversed output - } else if (lnL.1 >= 1) { - let (l, nL) = lnL; - let (r, nR) = rnR; - let targetSizeL = 2 * nL + 1; - let targetSizeR = nR - nL - 1 : Nat; - debug assert targetSizeL + targetSizeR == nL + nR; - let small = #small1(Current.new(l, targetSizeL), l, null); - let big = #big1(Current.new(r, targetSizeR), r, null, targetSizeR); - let states = (#right, big, small); // reversed output - let states6 = States.step(States.step(States.step(States.step(States.step(States.step(states)))))); - ?(#rebal(states6), x) - } else { - ?(Stacks.smallqueueReversed(rnR.0), x) // reversed output - } - }; - case (#rebal(dir, big0, small0)) switch dir { - case (#right) { - // ^ reversed input - let (x, small) = SmallState.pop(small0); - let states4 = States.step(States.step(States.step(States.step((#right, big0, small))))); // reversed output - debug assert states4.0 == #right; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(big, small); - ?(#idles(big, small), x) // reversed output - }; - case _ ?(#rebal(states4), x) - } - }; - case (#left) { - // ^ reversed input - let (x, big) = BigState.pop(big0); - let states4 = States.step(States.step(States.step(States.step((#left, big, small0))))); // reversed output - debug assert states4.0 == #left; - switch states4 { - case (_, #big2(#idle(_, big)), #small3(#idle(_, small))) { - debug assert idlesInvariant(small, big); - ?(#idles(small, big), x) // reversed output - }; - case _ ?(#rebal(states4), x) - } - } - }; - case (#empty) null; - case (#one(x)) ?(#empty, x); - case (#two(x, y)) ?(#one(x), y); - case (#three(x, y, z)) ?(#two(x, y), z) - }; - - /// Turn an iterator into a queue, consuming it. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([0, 1, 2, 3, 4].values()); - /// assert Queue.peekFront(queue) == ?0; - /// assert Queue.peekBack(queue) == ?4; - /// assert Queue.size(queue) == 5; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func fromIter(iter : Iter) : Queue { - var queue = empty(); - Iter.forEach(iter, func(t : T) = queue := pushBack(queue, t)); - queue - }; - - /// Convert an iterator into a queue, consuming the iterator. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// transient let iter = [0, 1, 2, 3, 4].values(); - /// - /// let queue = iter.toQueue(); - /// - /// assert Queue.peekFront(queue) == ?0; - /// assert Queue.peekBack(queue) == ?4; - /// assert Queue.size(queue) == 5; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func toQueue(self : Iter) : Queue { - fromIter(self) - }; - - /// Create an iterator over the elements in the queue. The order of the elements is from front to back. - /// - /// Example: - /// ```motoko include=import - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Iter.toArray(Queue.values(queue)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(1)` to create the iterator and for each `next()` call. - /// - /// Space: `O(1)` to create the iterator and for each `next()` call. - public func values(self : Queue) : Iter.Iter { - object { - var current = self; - public func next() : ?T { - switch (popFront(current)) { - case null null; - case (?result) { - current := result.1; - ?result.0 - } - } - } - } - }; - - /// Compare two queues for equality using a provided equality function to compare their elements. - /// Two queues are considered equal if they contain the same elements in the same order. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2, 3].values()); - /// let queue2 = Queue.fromIter([1, 2, 3].values()); - /// let queue3 = Queue.fromIter([1, 3, 2].values()); - /// assert Queue.equal(queue1, queue2, Nat.equal); - /// assert not Queue.equal(queue1, queue3, Nat.equal); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func equal(self : Queue, other : Queue, equal : (implicit : (T, T) -> Bool)) : Bool { - if (size(self) != size(other)) { - return false - }; - func go(self : Queue, other : Queue, equal : (T, T) -> Bool) : Bool = switch (popFront self, popFront other) { - case (null, null) true; - case (?(x1, tail1), ?(x2, tail2)) equal(x1, x2) and go(tail1, tail2, equal); // Note that this is tail recursive (`and` is expanded to `if`). - case _ false - }; - go(self, other, equal) - }; - - /// Compare two queues lexicographically using a provided comparison function to compare their elements. - /// Returns `#less` if `queue1` is lexicographically less than `queue2`, `#equal` if they are equal, and `#greater` otherwise. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue1 = Queue.fromIter([1, 2, 3].values()); - /// let queue2 = Queue.fromIter([1, 2, 4].values()); - /// assert Queue.compare(queue1, queue2, Nat.compare) == #less; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - public func compare(self : Queue, other : Queue, compareItem : (implicit : (compare : (T, T) -> Types.Order))) : Types.Order = switch (popFront self, popFront other) { - case (null, null) #equal; - case (null, _) #less; - case (_, null) #greater; - case (?(x1, selfTail), ?(x2, otherTail)) { - switch (compareItem(x1, x2)) { - case (#equal) compare(selfTail, otherTail, compareItem); - case order order - } - } - }; - - /// Return true if the given predicate is true for all queue elements. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([2, 4, 6].values()); - /// assert Queue.all(queue, func n = n % 2 == 0); - /// assert not Queue.all(queue, func n = n > 4); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` as the current implementation uses `values` to iterate over the queue. - /// - /// *Runtime and space assumes that the `predicate` runs in `O(1)` time and space. - public func all(self : Queue, predicate : T -> Bool) : Bool = switch self { - case (#empty) true; - case (#one(x)) predicate x; - case (#two(x, y)) predicate x and predicate y; - case (#three(x, y, z)) predicate x and predicate y and predicate z; - case _ { - for (item in values self) if (not (predicate item)) return false; - return true - } - }; - - /// Return true if the given predicate is true for any queue element. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.any(queue, func n = n > 2); - /// assert not Queue.any(queue, func n = n > 3); - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` as the current implementation uses `values` to iterate over the queue. - /// - /// *Runtime and space assumes that the `predicate` runs in `O(1)` time and space. - public func any(self : Queue, predicate : T -> Bool) : Bool = switch self { - case (#empty) false; - case (#one(x)) predicate x; - case (#two(x, y)) predicate x or predicate y; - case (#three(x, y, z)) predicate x or predicate y or predicate z; - case _ { - for (item in values self) if (predicate item) return true; - return false - } - }; - - /// Call the given function for its side effect on each queue element in order: from front to back. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// persistent actor { - /// var text = ""; - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// Queue.forEach(queue, func n = text #= Nat.toText(n)); - /// assert text == "123"; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func forEach(self : Queue, f : T -> ()) = switch self { - case (#empty) (); - case (#one(x)) f x; - case (#two(x, y)) { f x; f y }; - case (#three(x, y, z)) { f x; f y; f z }; - // Preserve the order when visiting the elements. Note that the #idles case would require reversing the second stack. - case _ { - for (t in values self) f t - } - }; - - /// Create a new queue by applying the given function to each element of the original queue. - /// - /// Note: The order of visiting elements is undefined with the current implementation. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let mapped = Queue.map(queue, func n = n * 2); - /// assert Queue.size(mapped) == 3; - /// assert Queue.peekFront(mapped) == ?2; - /// assert Queue.peekBack(mapped) == ?6; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `f` runs in `O(1)` time and space. - public func map(self : Queue, f : T1 -> T2) : Queue = switch self { - case (#empty) #empty; - case (#one(x)) #one(f x); - case (#two(x, y)) #two(f x, f y); - case (#three(x, y, z)) #three(f x, f y, f z); - case (#idles(l, r)) #idles(Idle.map(l, f), Idle.map(r, f)); - case (#rebal(_)) { - // No reason to rebuild the #rebal state. - // future work: It could be further optimized by building a balanced #idles state directly since we know the sizes. - var q = empty(); - for (t in values self) q := pushBack(q, f t); - q - } - }; - - /// Create a new queue with only those elements of the original queue for which - /// the given predicate returns true. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3, 4].values()); - /// let filtered = Queue.filter(queue, func n = n % 2 == 0); - /// assert Queue.size(filtered) == 2; - /// assert Queue.peekFront(filtered) == ?2; - /// assert Queue.peekBack(filtered) == ?4; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that `predicate` runs in `O(1)` time and space. - public func filter(self : Queue, predicate : T -> Bool) : Queue { - var q = empty(); - for (t in values self) if (predicate t) q := pushBack(q, t); - q - }; - - /// Create a new queue by applying the given function to each element of the original queue - /// and collecting the results for which the function returns a non-null value. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3, 4].values()); - /// let filtered = Queue.filterMap(queue, func n = if (n % 2 == 0) { ?n } else null); - /// assert Queue.size(filtered) == 2; - /// assert Queue.peekFront(filtered) == ?2; - /// assert Queue.peekBack(filtered) == ?4; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that f runs in `O(1)` time and space. - public func filterMap(self : Queue, f : T -> ?U) : Queue { - var q = empty(); - for (t in values self) { - switch (f t) { - case (?x) q := pushBack(q, x); - case null () - } - }; - q - }; - - /// Create a `Text` representation of a queue for debugging purposes. - /// - /// Example: - /// ```motoko include=import - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// assert Queue.toText(queue, Nat.toText) == "RealTimeQueue[1, 2, 3]"; - /// } - /// ``` - /// - /// Runtime: `O(size)` - /// - /// Space: `O(size)` - /// - /// *Runtime and space assumes that f runs in `O(1)` time and space. - public func toText(self : Queue, f : (implicit : (toText : T -> Text))) : Text { - var text = "RealTimeQueue["; - var first = true; - for (t in values self) { - if (first) first := false else text #= ", "; - text #= f(t) - }; - text # "]" - }; - - /// Reverse the order of elements in a queue. - /// This operation is cheap, it does NOT require copying the elements. - /// - /// Example: - /// ```motoko include=import - /// persistent actor { - /// let queue = Queue.fromIter([1, 2, 3].values()); - /// let reversed = Queue.reverse(queue); - /// assert Queue.peekFront(reversed) == ?3; - /// assert Queue.peekBack(reversed) == ?1; - /// } - /// ``` - /// - /// Runtime: `O(1)` - /// - /// Space: `O(1)` - public func reverse(self : Queue) : Queue = switch self { - case (#idles(l, r)) #idles(r, l); - case (#rebal(#left, big, small)) #rebal(#right, big, small); - case (#rebal(#right, big, small)) #rebal(#left, big, small); - case (#empty) self; - case (#one(_)) self; - case (#two(x, y)) #two(y, x); - case (#three(x, y, z)) #three(z, y, x) - }; - - type Stacks = (left : List, right : List); - - module Stacks { - public func push((left, right) : Stacks, t : T) : Stacks = (?(t, left), right); - - public func pop(stacks : Stacks) : Stacks = switch stacks { - case (?(_, leftTail), right) (leftTail, right); - case (null, ?(_, rightTail)) (null, rightTail); - case (null, null) stacks - }; - - public func first((left, right) : Stacks) : ?T = switch (left) { - case (?(h, _)) ?h; - case (null) do ? { right!.0 } - }; - - public func unsafeFirst((left, right) : Stacks) : T = switch (left) { - case (?(h, _)) h; - case (null) Option.unwrap(right).0 - }; - - public func isEmpty((left, right) : Stacks) : Bool = List.isEmpty(left) and List.isEmpty(right); - - public func size((left, right) : Stacks) : Nat = List.size(left) + List.size(right); - - public func smallqueue((left, right) : Stacks) : Queue = switch (left, right) { - case (null, null) #empty; - case (null, ?(x, null)) #one(x); - case (?(x, null), null) #one(x); - case (null, ?(x, ?(y, null))) #two(y, x); - case (?(x, null), ?(y, null)) #two(y, x); - case (?(x, ?(y, null)), null) #two(y, x); - case (null, ?(x, ?(y, ?(z, null)))) #three(z, y, x); - case (?(x, ?(y, ?(z, null))), null) #three(z, y, x); - case (?(x, ?(y, null)), ?(z, null)) #three(z, y, x); - case (?(x, null), ?(y, ?(z, null))) #three(z, y, x); - case _ (trap "Queue.Stacks.smallqueue() impossible") - }; - - public func smallqueueReversed((left, right) : Stacks) : Queue = switch (left, right) { - case (null, null) #empty; - case (null, ?(x, null)) #one(x); - case (?(x, null), null) #one(x); - case (null, ?(x, ?(y, null))) #two(x, y); - case (?(x, null), ?(y, null)) #two(x, y); - case (?(x, ?(y, null)), null) #two(x, y); - case (null, ?(x, ?(y, ?(z, null)))) #three(x, y, z); - case (?(x, ?(y, ?(z, null))), null) #three(x, y, z); - case (?(x, ?(y, null)), ?(z, null)) #three(x, y, z); - case (?(x, null), ?(y, ?(z, null))) #three(x, y, z); - case _ (trap "Queue.Stacks.smallqueueReversed() impossible") - }; - public func map((left, right) : Stacks, f : T -> U) : Stacks = (List.map(left, f), List.map(right, f)) - }; - - /// Represents an end of the queue that is not in a rebalancing process. It is a stack and its size. - type Idle = (stacks : Stacks, size : Nat); - module Idle { - public func push((stacks, size) : Idle, t : T) : Idle = (Stacks.push(stacks, t), 1 + size); - public func pop((stacks, size) : Idle) : (T, Idle) = (Stacks.unsafeFirst(stacks), (Stacks.pop(stacks), size - 1 : Nat)); - public func peek((stacks, _) : Idle) : T = Stacks.unsafeFirst(stacks); - - public func map((stacks, size) : Idle, f : T -> U) : Idle = (Stacks.map(stacks, f), size) - }; - - /// Stores information about operations that happen during rebalancing but which have not become part of the old state that is being rebalanced. - /// - /// - `extra`: newly added elements - /// - `extraSize`: size of `extra` - /// - `old`: elements contained before the rebalancing process - /// - `targetSize`: the number of elements which will be contained after the rebalancing is finished - type Current = (extra : List, extraSize : Nat, old : Stacks, targetSize : Nat); - - module Current { - public func new(old : Stacks, targetSize : Nat) : Current = (null, 0, old, targetSize); - - public func push((extra, extraSize, old, targetSize) : Current, t : T) : Current = (?(t, extra), 1 + extraSize, old, targetSize); - - public func pop((extra, extraSize, old, targetSize) : Current) : (T, Current) = switch (extra) { - case (?(h, t)) (h, (t, extraSize - 1 : Nat, old, targetSize)); - case (null) (Stacks.unsafeFirst(old), (null, extraSize, Stacks.pop(old), targetSize - 1 : Nat)) - }; - - public func peek((extra, _, old, _) : Current) : T = switch (extra) { - case (?(h, _)) h; - case (null) Stacks.unsafeFirst(old) - }; - - public func size((_, extraSize, _, targetSize) : Current) : Nat = extraSize + targetSize - }; - - /// The bigger end of the queue during rebalancing. It is used to split the bigger end of the queue into the new big end and a portion to be added to the small end. Can be in one of the following states: - /// - /// - `#big1(cur, big, aux, n)`: Initial state. Using the step function it takes `n`-elements from the `big` stack and puts them to `aux` in reversed order. `#big1(cur, x1 .. xn : bigTail, [], n) ->* #big1(cur, bigTail, xn .. x1, 0)`. The `bigTail` is later given to the `small` end. - /// - `#big2(common)`: Is used to reverse the elements from the previous phase to restore the original order. `common = #copy(cur, xn .. x1, [], 0) ->* #copy(cur, [], x1 .. xn, n)`. - type BigState = { - #big1 : (Current, Stacks, List, Nat); - #big2 : CommonState - }; - - module BigState { - public func push(big : BigState, t : T) : BigState = switch big { - case (#big1(cur, big, aux, n)) #big1(Current.push(cur, t), big, aux, n); - case (#big2(state)) #big2(CommonState.push(state, t)) - }; - - public func pop(big : BigState) : (T, BigState) = switch big { - case (#big1(cur, big, aux, n)) { - let (x, cur2) = Current.pop(cur); - (x, #big1(cur2, big, aux, n)) - }; - case (#big2(state)) { - let (x, state2) = CommonState.pop(state); - (x, #big2(state2)) - } - }; - - public func peek(big : BigState) : T = switch big { - case (#big1(cur, _, _, _)) Current.peek(cur); - case (#big2(state)) CommonState.peek(state) - }; - - public func step(big : BigState) : BigState = switch big { - case (#big1(cur, big, aux, n)) { - if (n == 0) - #big2(CommonState.norm(#copy(cur, aux, null, 0))) else - #big1(cur, Stacks.pop(big), ?(Stacks.unsafeFirst(big), aux), n - 1 : Nat) - }; - case (#big2(state)) #big2(CommonState.step(state)) - }; - - public func size(big : BigState) : Nat = switch big { - case (#big1(cur, _, _, _)) Current.size(cur); - case (#big2(state)) CommonState.size(state) - }; - - public func current(big : BigState) : Current = switch big { - case (#big1(cur, _, _, _)) cur; - case (#big2(state)) CommonState.current(state) - } - }; - - /// The smaller end of the queue during rebalancing. Can be in one of the following states: - /// - /// - `#small1(cur, small, aux)`: Initial state. Using the step function the original elements are reversed. `#small1(cur, s1 .. sn, []) ->* #small1(cur, [], sn .. s1)`, note that `aux` is initially empty, at the end contains the reversed elements from the small stack. - /// - `#small2(cur, aux, big, new, size)`: Using the step function the newly transfered tail from the bigger end is reversed on top of the `new` list. `#small2(cur, sn .. s1, b1 .. bm, [], 0) ->* #small2(cur, sn .. s1, [], bm .. b1, m)`, note that `aux` is the reversed small stack from the previous phase, `new` is initially empty, `size` corresponds to the size of `new`. - /// - `#small3(common)`: Is used to reverse the elements from the two previous phases again to get them again in the original order. `#copy(cur, sn .. s1, bm .. b1, m) ->* #copy(cur, [], s1 .. sn : bm .. b1, n + m)`, note that the correct order of the elements from the big stack is reversed. - type SmallState = { - #small1 : (Current, Stacks, List); - #small2 : (Current, List, Stacks, List, Nat); - #small3 : CommonState - }; - - module SmallState { - public func push(state : SmallState, t : T) : SmallState = switch state { - case (#small1(cur, small, aux)) #small1(Current.push(cur, t), small, aux); - case (#small2(cur, aux, big, new, newN)) #small2(Current.push(cur, t), aux, big, new, newN); - case (#small3(common)) #small3(CommonState.push(common, t)) - }; - - public func pop(state : SmallState) : (T, SmallState) = switch state { - case (#small1(cur0, small, aux)) { - let (t, cur) = Current.pop(cur0); - (t, #small1(cur, small, aux)) - }; - case (#small2(cur0, aux, big, new, newN)) { - let (t, cur) = Current.pop(cur0); - (t, #small2(cur, aux, big, new, newN)) - }; - case (#small3(common0)) { - let (t, common) = CommonState.pop(common0); - (t, #small3(common)) - } - }; - - public func peek(state : SmallState) : T = switch state { - case (#small1(cur, _, _)) Current.peek(cur); - case (#small2(cur, _, _, _, _)) Current.peek(cur); - case (#small3(common)) CommonState.peek(common) - }; - - public func step(state : SmallState) : SmallState = switch state { - case (#small1(cur, small, aux)) { - if (Stacks.isEmpty(small)) state else #small1(cur, Stacks.pop(small), ?(Stacks.unsafeFirst(small), aux)) - }; - case (#small2(cur, aux, big, new, newN)) { - if (Stacks.isEmpty(big)) #small3(CommonState.norm(#copy(cur, aux, new, newN))) else #small2(cur, aux, Stacks.pop(big), ?(Stacks.unsafeFirst(big), new), 1 + newN) - }; - case (#small3(common)) #small3(CommonState.step(common)) - }; - - public func size(state : SmallState) : Nat = switch state { - case (#small1(cur, _, _)) Current.size(cur); - case (#small2(cur, _, _, _, _)) Current.size(cur); - case (#small3(common)) CommonState.size(common) - }; - - public func current(state : SmallState) : Current = switch state { - case (#small1(cur, _, _)) cur; - case (#small2(cur, _, _, _, _)) cur; - case (#small3(common)) CommonState.current(common) - } - }; - - type CopyState = { #copy : (Current, List, List, Nat) }; - - /// Represents the last rebalancing phase of both small and big ends of the queue. It is used to reverse the elements from the previous phases to restore the original order. It can be in one of the following states: - /// - /// - `#copy(cur, aux, new, sizeOfNew)`: Puts the elements from `aux` in reversed order on top of `new`. `#copy(cur, xn .. x1, new, sizeOfNew) ->* #copy(cur, [], x1 .. xn : new, n + sizeOfNew)`. - /// - `#idle(cur, idle)`: The rebalancing process is done and the queue is in the idle state. - type CommonState = CopyState or { #idle : (Current, Idle) }; - - module CommonState { - public func step(common : CommonState) : CommonState = switch common { - case (#copy copy) { - let (cur, aux, new, sizeOfNew) = copy; - let (_, _, _, targetSize) = cur; - norm(if (sizeOfNew < targetSize) #copy(cur, unsafeTail(aux), ?(unsafeHead(aux), new), 1 + sizeOfNew) else #copy copy) - }; - case (#idle _) common - }; - - public func norm(copy : CopyState) : CommonState { - let #copy(cur, _, new, sizeOfNew) = copy; - let (extra, extraSize, _, targetSize) = cur; - debug assert sizeOfNew <= targetSize; - if (sizeOfNew >= targetSize) { - #idle(cur, ((extra, new), extraSize + sizeOfNew)) // note: aux can be non-empty, thus ignored here, when the target size decreases after pop operations - } else copy - }; - - public func push(common : CommonState, t : T) : CommonState = switch common { - case (#copy(cur, aux, new, sizeOfNew)) #copy(Current.push(cur, t), aux, new, sizeOfNew); - case (#idle(cur, idle)) #idle(Current.push(cur, t), Idle.push(idle, t)) // yes, push to both - }; - - public func pop(common : CommonState) : (T, CommonState) = switch common { - case (#copy(cur, aux, new, sizeOfNew)) { - let (t, cur2) = Current.pop(cur); - (t, norm(#copy(cur2, aux, new, sizeOfNew))) - }; - case (#idle(cur, idle)) { - let (t, idle2) = Idle.pop(idle); - (t, #idle(Current.pop(cur).1, idle2)) - } - }; - - public func peek(common : CommonState) : T = switch common { - case (#copy(cur, _, _, _)) Current.peek(cur); - case (#idle(_, idle)) Idle.peek(idle) - }; - - public func size(common : CommonState) : Nat = switch common { - case (#copy(cur, _, _, _)) Current.size(cur); - case (#idle(_, (_, size))) size - }; - - public func current(common : CommonState) : Current = switch common { - case (#copy(cur, _, _, _)) cur; - case (#idle(cur, _)) cur - } - }; - - type States = ( - direction : Direction, - bigState : BigState, - smallState : SmallState - ); - - module States { - public func step(states : States) : States = switch states { - case (dir, #big1(_, bigTail, _, 0), #small1(currentS, _, auxS)) { - (dir, BigState.step(states.1), #small2(currentS, auxS, bigTail, null, 0)) - }; - case (dir, big, small) (dir, BigState.step(big), SmallState.step(small)) - } - }; - - type Direction = { #left; #right }; - - func idlesInvariant(((l, nL), (r, nR)) : (Idle, Idle)) : Bool = Stacks.size(l) == nL and Stacks.size(r) == nR and 3 * nL >= nR and 3 * nR >= nL; - - type List = Types.Pure.List; - type Iter = Types.Iter; - func unsafeHead(l : List) : T = Option.unwrap(l).0; - func unsafeTail(l : List) : List = Option.unwrap(l).1 -} diff --git a/.mops/core@2.5.0/src/pure/Set.mo b/.mops/core@2.5.0/src/pure/Set.mo deleted file mode 100644 index 020c79a..0000000 --- a/.mops/core@2.5.0/src/pure/Set.mo +++ /dev/null @@ -1,1563 +0,0 @@ -/// Pure (immutable) sets based on order/comparison of elements. -/// A set is a collection of elements without duplicates. -/// The set data structure type is stable and can be used for orthogonal persistence. -/// -/// Example: -/// ```motoko -/// import Set "mo:core/pure/Set"; -/// import Nat "mo:core/Nat"; -/// -/// persistent actor { -/// let set = Set.fromIter([3, 1, 2, 3].values(), Nat.compare); -/// assert Set.size(set) == 3; -/// assert not Set.contains(set, Nat.compare, 4); -/// let diff = Set.difference(set, set, Nat.compare); -/// assert Set.isEmpty(diff); -/// } -/// ``` -/// -/// These sets are implemented as red-black trees, a balanced binary search tree of ordered elements. -/// -/// The tree data structure internally colors each of its nodes either red or black, -/// and uses this information to balance the tree during modifying operations. -/// -/// Performance: -/// * Runtime: `O(log(n))` worst case cost per insertion, removal, and retrieval operation. -/// * Space: `O(n)` for storing the entire tree. -/// `n` denotes the number of elements (i.e. nodes) stored in the tree. -/// -/// Credits: -/// -/// The core of this implementation is derived from: -/// -/// * Ken Friis Larsen's [RedBlackMap.sml](https://github.com/kfl/mosml/blob/master/src/mosmllib/Redblackmap.sml), which itself is based on: -/// * Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](http://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html). - -import Runtime "../Runtime"; -import List "../List"; // NB: imperative! -import Iter "../Iter"; -import Types "../Types"; -import Nat "../Nat"; -import Order "../Order"; - -module { - - /// Ordered collection of unique elements of the generic type `T`. - /// If type `T` is stable then `Set` is also stable. - /// To ensure that property the `Set` does not have any methods, - /// instead they are gathered in the functor-like class `Operations` (see example there). - - /// @deprecated M0235 - public type Set = Types.Pure.Set; - - /// Red-black tree of nodes with ordered set elements. - /// Leaves are considered implicitly black. - type Tree = Types.Pure.Set.Tree; - - /// Create a set with the elements obtained from an iterator. - /// Potential duplicate elements in the iterator are ignored, i.e. - /// multiple occurrences of an equal element only occur once in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([3, 1, 2, 1].values(), Nat.compare); - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func fromIter(iter : Iter.Iter, compare : (implicit : (T, T) -> Order.Order)) : Set { - var set = empty() : Set; - for (val in iter) { - set := Internal.add(set, compare, val) - }; - set - }; - - /// Convert an iterator into a set. - /// Potential duplicate elements in the iterator are ignored, i.e. - /// multiple occurrences of an equal element only occur once in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// transient let iter = [3, 1, 2, 1].values(); - /// - /// let set = iter.toSet(Nat.compare); - /// - /// assert Iter.toArray(Set.values(set)) == [1, 2, 3]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements returned by the iterator and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func toSet(self : Iter.Iter, compare : (implicit : (T, T) -> Order.Order)) : Set { - fromIter(self, compare) - }; - - /// Given a `set` ordered by `compare`, insert the new `element`, - /// returning the new set. - /// - /// Return the set unchanged if the element already exists in the set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set0 = Set.empty(); - /// let set1 = Set.add(set0, Nat.compare, 2); - /// let set2 = Set.add(set1, Nat.compare, 1); - /// let set3 = Set.add(set2, Nat.compare, 2); - /// assert Iter.toArray(Set.values(set0)) == []; - /// assert Iter.toArray(Set.values(set1)) == [2]; - /// assert Iter.toArray(Set.values(set2)) == [1, 2]; - /// assert Iter.toArray(Set.values(set3)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned set shares with the `set` most of the tree nodes. - /// Garbage collecting one of the sets (e.g. after an assignment `m := Set.add(m, c, e)`) - /// causes collecting `O(log(n))` nodes. - public func add(self : Set, compare : (implicit : (T, T) -> Order.Order), elem : T) : Set = Internal.add(self, compare, elem); - - /// Given `set` ordered by `compare`, insert the new `element`, - /// returning the set extended with `element` and a Boolean indicating - /// if the element was already present in `set`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set0 = Set.empty(); - /// do { - /// let (set1, new1) = Set.insert(set0, Nat.compare, 2); - /// assert new1; - /// let (set2, new2) = Set.insert(set1, Nat.compare, 1); - /// assert new2; - /// let (set3, new3) = Set.insert(set2, Nat.compare, 2); - /// assert not new3; - /// assert Iter.toArray(Set.values(set3)) == [1, 2] - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: The returned set shares with the `set` most of the tree nodes. - /// Garbage collecting one of the sets (e.g. after an assignment `m := Set.add(m, c, e)`) - /// causes collecting `O(log(n))` nodes. - public func insert(self : Set, compare : (implicit : (T, T) -> Order.Order), elem : T) : (Set, Bool) = Internal.insert(self, compare, elem); - - /// Given `set` ordered by `compare` return the set with `element` removed. - /// Return the set unchanged if the element was absent. - /// - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// - /// let set1 = Set.remove(set, Nat.compare, 2); - /// let set2 = Set.remove(set1, Nat.compare, 4); - /// assert Iter.toArray(Set.values(set2)) == [1, 3]; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// Note: The returned set shares with `set` most of the tree nodes. - /// Garbage collecting one of the sets (e.g. after an assignment `m := Set.delete(m, c, e)`) - /// causes collecting `O(log(n))` nodes. - public func remove(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Set = Internal.remove(self, compare, element); - - /// Given `set` ordered by `compare`, delete `element` from the set, returning - /// either the set without the element and a Boolean indicating whether - /// whether `element` was contained in `set`. - /// - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// do { - /// let (set1, contained1) = Set.delete(set, Nat.compare, 2); - /// assert contained1; - /// assert Iter.toArray(Set.values(set1)) == [1, 3]; - /// let (set2, contained2) = Set.delete(set1, Nat.compare, 4); - /// assert not contained2; - /// assert Iter.toArray(Set.values(set2)) == [1, 3]; - /// } - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(log(n))` including garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(log(n))` objects that will be collected as garbage. - /// Note: The returned set shares with `set` most of the tree nodes. - /// Garbage collecting one of the sets (e.g. after an assignment `m := Set.delete(m, c, e)`) - /// causes collecting `O(log(n))` nodes. - public func delete(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : (Set, Bool) = Internal.delete(self, compare, element); - - /// Tests whether the set contains the provided element. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Bool "mo:core/Bool"; - /// - /// persistent actor { - /// let set = Set.fromIter([3, 1, 2].values(), Nat.compare); - /// - /// assert Set.contains(set, Nat.compare, 1); - /// assert not Set.contains(set, Nat.compare, 4); - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func contains(self : Set, compare : (implicit : (T, T) -> Order.Order), element : T) : Bool = Internal.contains(self.root, compare, element); - - /// Get the maximal element of the set `set` if it is not empty, otherwise returns `null` - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 2, 1].values(), Nat.compare); - /// let set2 = Set.empty(); - /// assert Set.max(set1) == ?2; - /// assert Set.max(set2) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements in the set - public func max(self : Set) : ?T = Internal.max(self.root); - - /// Retrieves the minimum element from the set. - /// If the set is empty, returns `null`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([2, 0, 1].values(), Nat.compare); - /// let set2 = Set.empty(); - /// assert Set.min(set1) == ?0; - /// assert Set.min(set2) == null; - /// } - /// ``` - /// - /// Runtime: `O(log(n))`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set. - public func min(self : Set) : ?T = Internal.min(self.root); - - /// Returns a new set that is the union of `set1` and `set2`, - /// i.e. a new set that all the elements that exist in at least on of the two sets. - /// Potential duplicates are ignored, i.e. if the same element occurs in both `set1` - /// and `set2`, it only occurs once in the returned set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let union = Set.union(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(union)) == [1, 2, 3, 4, 5]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(m)`, retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements in the sets, and `m <= n`. - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(m * log(n))` temporary objects that will be collected as garbage. - public func union(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - if (size(self) < size(other)) { - foldLeft(self, other, func(acc : Set, elem : T) : Set { Internal.add(acc, compare, elem) }) - } else { - foldLeft(other, self, func(acc : Set, elem : T) : Set { Internal.add(acc, compare, elem) }) - } - }; - - /// Returns a new set that is the intersection of `set1` and `set2`, - /// i.e. a new set that contains all the elements that exist in both sets. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let intersection = Set.intersection(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(intersection)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(m)` temporary objects that will be collected as garbage. - public func intersection(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - let elems = List.empty(); - if (self.size < other.size) { - Internal.iterate( - self.root, - func(x : T) { - if (Internal.contains(other.root, compare, x)) { - List.add(elems, x) - } - } - ) - } else { - Internal.iterate( - other.root, - func(x : T) { - if (Internal.contains(self.root, compare, x)) { - List.add(elems, x) - } - } - ) - }; - { root = Internal.buildFromSorted(elems); size = List.size(elems) } - }; - - /// Returns a new set that is the difference between `set1` and `other` (`set1` minus `set2`), - /// i.e. a new set that contains all the elements of `set1` that do not exist in `set2`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let difference = Set.difference(set1, set2, Nat.compare); - /// assert Iter.toArray(Set.values(difference)) == [1, 2]; - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets `set1` and `set2`, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(m * log(n))` temporary objects that will be collected as garbage. - public func difference(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Set { - if (size(self) < size(other)) { - let elems = List.empty(); /* imperative! */ - Internal.iterate( - self.root, - func(x : T) { - if (not Internal.contains(other.root, compare, x)) { - List.add(elems, x) - } - } - ); - { root = Internal.buildFromSorted(elems); size = List.size(elems) } - } else { - foldLeft( - other, - self, - func(acc : Set, elem : T) : Set { - if (Internal.contains(acc.root, compare, elem)) { - Internal.remove(acc, compare, elem) - } else { acc } - } - ) - } - }; - - /// Project all elements of the set in a new set. - /// Apply a mapping function to each element in the set and - /// collect the mapped elements in a new mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([3, 1, 2].values(), Nat.compare); - /// - /// let textNumbers = - /// Set.map(numbers, Text.compare, Nat.toText); - /// assert Iter.toArray(Set.values(textNumbers)) == ["1", "2", "3"]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func map(self : Set, compare : (implicit : (T2, T2) -> Order.Order), project : T1 -> T2) : Set = Internal.foldLeft(self.root, empty(), func(acc : Set, elem : T1) : Set { Internal.add(acc, compare, project(elem)) }); - - /// Apply an operation on each element contained in the set. - /// The operation is applied in ascending order of the elements. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// var text = ""; - /// Set.forEach(numbers, func (element) { - /// text #= " " # Nat.toText(element) - /// }); - /// assert text == " 0 1 2 3"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory. - /// where `n` denotes the number of elements stored in the set. - /// - public func forEach(self : Set, operation : T -> ()) { - ignore foldLeft(self, null, func(acc, e) : Null { operation(e); null }) - }; - - /// Filter elements in a new set. - /// Create a copy of the mutable set that only contains the elements - /// that fulfil the criterion function. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let evenNumbers = Set.filter(numbers, Nat.compare, func (number) { - /// number % 2 == 0 - /// }); - /// assert Iter.toArray(Set.values(evenNumbers)) == [0, 2]; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)`. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - public func filter(self : Set, compare : (implicit : (T, T) -> Order.Order), criterion : T -> Bool) : Set { - foldLeft>( - self, - empty(), - func(acc, e) { - if (criterion(e)) (add(acc, compare, e)) else acc - } - ) - }; - - /// Filter all elements in the set by also applying a projection to the elements. - /// Apply a mapping function `project` to all elements in the set and collect all - /// elements, for which the function returns a non-null new element. Collect all - /// non-discarded new elements in a new mutable set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Text "mo:core/Text"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let numbers = Set.fromIter([3, 0, 2, 1].values(), Nat.compare); - /// - /// let evenTextNumbers = Set.filterMap(numbers, Text.compare, func (number) { - /// if (number % 2 == 0) { - /// ?Nat.toText(number) - /// } else { - /// null // discard odd numbers - /// } - /// }); - /// assert Iter.toArray(Set.values(evenTextNumbers)) == ["0", "2"]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - /// Runtime: `O(n * log(n))`. - /// Space: `O(n)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that the `compare` function implements an `O(1)` comparison. - /// - /// Note: Creates `O(n * log(n))` temporary objects that will be collected as garbage. - public func filterMap(self : Set, compare : (implicit : (T2, T2) -> Order.Order), project : T1 -> ?T2) : Set { - func combine(acc : Set, elem : T1) : Set { - switch (project(elem)) { - case null { acc }; - case (?elem2) { - Internal.add(acc, compare, elem2) - } - } - }; - Internal.foldLeft(self.root, empty(), combine) - }; - - /// Test whether `set1` is a sub-set of `set2`, i.e. each element in `set1` is - /// also contained in `set2`. Returns `true` if both sets are equal. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([2, 1, 0].values(), Nat.compare); - /// let set3 = Set.fromIter([3, 4].values(), Nat.compare); - /// assert Set.isSubset(set1, set2, Nat.compare); - /// assert not Set.isSubset(set1, set3, Nat.compare); - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets set1 and set2, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func isSubset(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Bool { - if (self.size > other.size) { return false }; - isSubsetHelper(self.root, other.root, compare) - }; - - /// Test whether two sets are equal. - /// Both sets have to be constructed by the same comparison function. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2].values(), Nat.compare); - /// let set2 = Set.fromIter([2, 1].values(), Nat.compare); - /// let set3 = Set.fromIter([2, 1, 0].values(), Nat.compare); - /// assert Set.equal(set1, set2, Nat.compare); - /// assert not Set.equal(set1, set3, Nat.compare); - /// } - /// ``` - /// - /// Runtime: `O(m * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `m` and `n` denote the number of elements stored in the sets set1 and set2, respectively, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func equal(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Bool { - if (self.size != other.size) { return false }; - isSubsetHelper(self.root, other.root, compare) - }; - - func isSubsetHelper(t1 : Tree, t2 : Tree, compare : (T, T) -> Order.Order) : Bool { - switch (t1, t2) { - case (#leaf, _) { true }; - case (_, #leaf) { false }; - case ((#red(t1l, x1, t1r) or #black(t1l, x1, t1r)), (#red(t2l, x2, t2r)) or #black(t2l, x2, t2r)) { - switch (compare(x1, x2)) { - case (#equal) { - isSubsetHelper(t1l, t2l, compare) and isSubsetHelper(t1r, t2r, compare) - }; - // x1 < x2 ==> x1 \in t2l /\ t1l \subset t2l - case (#less) { - Internal.contains(t2l, compare, x1) and isSubsetHelper(t1l, t2l, compare) and isSubsetHelper(t1r, t2, compare) - }; - // x2 < x1 ==> x1 \in t2r /\ t1r \subset t2r - case (#greater) { - Internal.contains(t2r, compare, x1) and isSubsetHelper(t1l, t2, compare) and isSubsetHelper(t1r, t2r, compare) - } - } - } - } - }; - - /// Compare two sets by comparing the elements. - /// Both sets must have been created by the same comparison function. - /// The two sets are iterated by the ascending order of their creation and - /// order is determined by the following rules: - /// Less: - /// `set1` is less than `set2` if: - /// * the pairwise iteration hits an element pair `element1` and `element2` where - /// `element1` is less than `element2` and all preceding elements are equal, or, - /// * `set1` is a strict prefix of `set2`, i.e. `set2` has more elements than `set1` - /// and all elements of `set1` occur at the beginning of iteration `set2`. - /// Equal: - /// `set1` and `set2` have same series of equal elements by pairwise iteration. - /// Greater: - /// `set1` is neither less nor equal `set2`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([0, 1].values(), Nat.compare); - /// let set2 = Set.fromIter([0, 2].values(), Nat.compare); - /// - /// assert Set.compare(set1, set2, Nat.compare) == #less; - /// assert Set.compare(set1, set1, Nat.compare) == #equal; - /// assert Set.compare(set2, set1, Nat.compare) == #greater; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that `compare` has runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func compare(self : Set, other : Set, compare : (implicit : (T, T) -> Order.Order)) : Order.Order { - // TODO: optimize using recursion on self? - let iterator1 = values(self); - let iterator2 = values(other); - loop { - switch (iterator1.next(), iterator2.next()) { - case (null, null) return #equal; - case (null, _) return #less; - case (_, null) return #greater; - case (?element1, ?element2) { - let comparison = compare(element1, element2); - if (comparison != #equal) { - return comparison - } - } - } - } - }; - - /// Returns an iterator over the elements in the set, - /// traversing the elements in the ascending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 3, 1].values(), Nat.compare); - /// - /// var text = ""; - /// for (number in Set.values(set)) { - /// text #= " " # Nat.toText(number); - /// }; - /// assert text == " 0 1 2 3"; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func values(self : Set) : Iter.Iter = Internal.iter(self.root, #fwd); - - /// Returns an iterator over the elements in the set, - /// traversing the elements in the descending order. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 2, 3, 1].values(), Nat.compare); - /// - /// var tmp = ""; - /// for (number in Set.reverseValues(set)) { - /// tmp #= " " # Nat.toText(number); - /// }; - /// assert tmp == " 3 2 1 0"; - /// } - /// ``` - /// Cost of iteration over all elements: - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func reverseValues(self : Set) : Iter.Iter = Internal.iter(self.root, #bwd); - - /// Create a new empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.empty(); - /// assert Iter.toArray(Set.values(set)) == []; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func empty() : Set = { root = #leaf; size = 0 }; - - /// Create a new set with a single element. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.singleton(0); - /// assert Iter.toArray(Set.values(set)) == [0]; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func singleton(element : T) : Set { - { - size = 1; - root = #red(#leaf, element, #leaf) - } - }; - - /// Return the number of elements in a set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1, 3].values(), Nat.compare); - /// - /// assert Set.size(set) == 4; - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func size(self : Set) : Nat = self.size; - - /// Iterate all elements in ascending order, - /// and accumulate the elements by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1].values(), Nat.compare); - /// - /// let text = Set.foldLeft( - /// set, - /// "", - /// func (accumulator, element) { - /// accumulator # " " # Nat.toText(element) - /// } - /// ); - /// assert text == " 0 1 2 3"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - public func foldLeft( - self : Set, - base : A, - combine : (A, T) -> A - ) : A = Internal.foldLeft(self.root, base, combine); - - /// Iterate all elements in descending order, - /// and accumulate the elements by applying the combine function, starting from a base value. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 2, 1].values(), Nat.compare); - /// - /// let text = Set.foldRight( - /// set, - /// "", - /// func (element, accumulator) { - /// accumulator # " " # Nat.toText(element) - /// } - /// ); - /// assert text == " 3 2 1 0"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set. - public func foldRight( - self : Set, - base : A, - combine : (T, A) -> A - ) : A = Internal.foldRight(self.root, base, combine); - - /// Determines whether a set is empty. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set1 = Set.empty(); - /// let set2 = Set.singleton(1); - /// - /// assert Set.isEmpty(set1); - /// assert not Set.isEmpty(set2); - /// } - /// ``` - /// - /// Runtime: `O(1)`. - /// Space: `O(1)`. - public func isEmpty(self : Set) : Bool { - switch (self.root) { - case (#leaf) { true }; - case _ { false } - } - }; - - /// Check whether all element in the set satisfy a predicate, i.e. - /// the `predicate` function returns `true` for all elements in the set. - /// Returns `true` for an empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let belowTen = Set.all(set, func (number) { - /// number < 10 - /// }); - /// assert belowTen; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - /// where `n` denotes the number of elements stored in the set. - public func all(self : Set, predicate : T -> Bool) : Bool = Internal.all(self.root, predicate); - - /// Check whether at least one element in the set satisfies a predicate, i.e. - /// the `predicate` function returns `true` for at least one element in the set. - /// Returns `false` for an empty set. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// let aboveTen = Set.any(set, func (number) { - /// number > 10 - /// }); - /// assert not aboveTen; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(1)`. - public func any(self : Set, pred : T -> Bool) : Bool = Internal.any(self.root, pred); - - /// Test helper that check internal invariant for the given set `s`. - /// Raise an error (for a stack trace) if invariants are violated. - public func assertValid(self : Set, compare : (implicit : (T, T) -> Order.Order)) : () { - Internal.assertValid(self, compare) - }; - - /// Generate a textual representation of all the elements in the set. - /// Primarily to be used for testing and debugging. - /// The elements are formatted according to `elementFormat`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set = Set.fromIter([0, 3, 1, 2].values(), Nat.compare); - /// - /// assert Set.toText(set, Nat.toText) == "PureSet{0, 1, 2, 3}"; - /// } - /// ``` - /// - /// Runtime: `O(n)`. - /// Space: `O(n)` retained memory plus garbage, see below. - /// where `n` denotes the number of elements stored in the set and - /// assuming that `elementFormat` has runtime and space costs of `O(1)`. - /// - /// Note: Creates `O(log(n))` temporary objects that will be collected as garbage. - public func toText(self : Set, elementFormat : (implicit : (toText : T -> Text))) : Text { - var text = "PureSet{"; - var sep = ""; - for (element in values(self)) { - text #= sep # elementFormat(element); - sep := ", " - }; - text # "}" - }; - - /// Construct the union of a set of element sets, i.e. all elements of - /// each element set are included in the result set. - /// Any duplicates are ignored, i.e. if the same element occurs in multiple element sets, - /// it only occurs once in the result set. - /// - /// Assumes all sets are ordered by `compare`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Order "mo:core/Order"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// func setCompare(first: Set.Set, second: Set.Set) : Order.Order { - /// Set.compare(first, second, Nat.compare) - /// }; - /// - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let set3 = Set.fromIter([5, 6, 7].values(), Nat.compare); - /// let setOfSets = Set.fromIter([set1, set2, set3].values(), setCompare); - /// let flatSet = Set.flatten(setOfSets, Nat.compare); - /// assert Iter.toArray(Set.values(flatSet)) == [1, 2, 3, 4, 5, 6, 7]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in all the sub-sets, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func flatten(self : Set>, compare : (implicit : (T, T) -> Order.Order)) : Set { - var result = empty(); - for (set in values(self)) { - result := union(result, set, compare) - }; - result - }; - - /// Construct the union of a series of sets, i.e. all elements of - /// each set are included in the result set. - /// Any duplicates are ignored, i.e. if an element occurs - /// in several of the iterated sets, it only occurs once in the result set. - /// - /// Assumes all sets are ordered by `compare`. - /// - /// Example: - /// ```motoko - /// import Set "mo:core/pure/Set"; - /// import Nat "mo:core/Nat"; - /// import Iter "mo:core/Iter"; - /// - /// persistent actor { - /// let set1 = Set.fromIter([1, 2, 3].values(), Nat.compare); - /// let set2 = Set.fromIter([3, 4, 5].values(), Nat.compare); - /// let set3 = Set.fromIter([5, 6, 7].values(), Nat.compare); - /// let combined = Set.join([set1, set2, set3].values(), Nat.compare); - /// assert Iter.toArray(Set.values(combined)) == [1, 2, 3, 4, 5, 6, 7]; - /// } - /// ``` - /// - /// Runtime: `O(n * log(n))`. - /// Space: `O(1)` retained memory plus garbage, see the note below. - /// where `n` denotes the number of elements stored in the iterated sets, - /// and assuming that the `compare` function implements an `O(1)` comparison. - public func join(self : Iter.Iter>, compare : (implicit : (T, T) -> Order.Order)) : Set { - var result = empty(); - for (set in self) { - result := union(result, set, compare) - }; - result - }; - - module Internal { - public func contains(tree : Tree, compare : (T, T) -> Order.Order, elem : T) : Bool { - func f(t : Tree, x : T) : Bool { - switch t { - case (#black(l, x1, r)) { - switch (compare(x, x1)) { - case (#less) { f(l, x) }; - case (#equal) { true }; - case (#greater) { f(r, x) } - } - }; - case (#red(l, x1, r)) { - switch (compare(x, x1)) { - case (#less) { f(l, x) }; - case (#equal) { true }; - case (#greater) { f(r, x) } - } - }; - case (#leaf) { false } - } - }; - f(tree, elem) - }; - - public func max(m : Tree) : ?V { - func rightmost(m : Tree) : V { - switch m { - case (#red(_, v, #leaf)) { v }; - case (#red(_, _, r)) { rightmost(r) }; - case (#black(_, v, #leaf)) { v }; - case (#black(_, _, r)) { rightmost(r) }; - case (#leaf) { Runtime.trap "pure/Set.max() impossible" } - } - }; - switch m { - case (#leaf) { null }; - case (_) { ?rightmost(m) } - } - }; - - public func min(m : Tree) : ?V { - func leftmost(m : Tree) : V { - switch m { - case (#red(#leaf, v, _)) { v }; - case (#red(l, _, _)) { leftmost(l) }; - case (#black(#leaf, v, _)) { v }; - case (#black(l, _, _)) { leftmost(l) }; - case (#leaf) { Runtime.trap "pure/Set.min() impossible" } - } - }; - switch m { - case (#leaf) { null }; - case (_) { ?leftmost(m) } - } - }; - - public func all(m : Tree, pred : V -> Bool) : Bool { - switch m { - case (#red(l, v, r)) { - pred(v) and all(l, pred) and all(r, pred) - }; - case (#black(l, v, r)) { - pred(v) and all(l, pred) and all(r, pred) - }; - case (#leaf) { true } - } - }; - - public func any(m : Tree, pred : V -> Bool) : Bool { - switch m { - case (#red(l, v, r)) { - pred(v) or any(l, pred) or any(r, pred) - }; - case (#black(l, v, r)) { - pred(v) or any(l, pred) or any(r, pred) - }; - case (#leaf) { false } - } - }; - - public func iterate(m : Tree, f : V -> ()) { - switch m { - case (#leaf) {}; - case (#black(l, v, r)) { iterate(l, f); f(v); iterate(r, f) }; - case (#red(l, v, r)) { iterate(l, f); f(v); iterate(r, f) } - } - }; - - // build tree from elements arr[l]..arr[r-1] - public func buildFromSorted(buf : List.List) : Tree { - var maxDepth = 0; - var maxSize = 1; - while (maxSize < List.size(buf)) { - maxDepth += 1; - maxSize += maxSize + 1 - }; - maxDepth := if (maxDepth == 0) { 1 } else { maxDepth }; // keep root black for 1 element tree - func buildFromSortedHelper(l : Nat, r : Nat, depth : Nat) : Tree { - if (l + 1 == r) { - if (depth == maxDepth) { - return #red(#leaf, List.at(buf, l), #leaf) - } else { - return #black(#leaf, List.at(buf, l), #leaf) - } - }; - if (l >= r) { - return #leaf - }; - let m = (l + r) / 2; - return #black( - buildFromSortedHelper(l, m, depth + 1), - List.at(buf, m), - buildFromSortedHelper(m + 1, r, depth + 1) - ) - }; - buildFromSortedHelper(0, List.size(buf), 0) - }; - - type IterRep = Types.Pure.List<{ #tr : Tree; #x : T }>; - - type SetTraverser = (Tree, T, Tree, IterRep) -> IterRep; - - class IterSet(tree : Tree, setTraverser : SetTraverser) { - var trees : IterRep = ?(#tr(tree), null); - public func next() : ?T { - switch (trees) { - case (null) { null }; - case (?(#tr(#leaf), ts)) { - trees := ts; - next() - }; - case (?(#x(x), ts)) { - trees := ts; - ?x - }; - case (?(#tr(#black(l, x, r)), ts)) { - trees := setTraverser(l, x, r, ts); - next() - }; - case (?(#tr(#red(l, x, r)), ts)) { - trees := setTraverser(l, x, r, ts); - next() - } - } - } - }; - - public func iter(s : Tree, direction : { #fwd; #bwd }) : Iter.Iter { - let turnLeftFirst : SetTraverser = func(l, x, r, ts) { - ?(#tr(l), ?(#x(x), ?(#tr(r), ts))) - }; - - let turnRightFirst : SetTraverser = func(l, x, r, ts) { - ?(#tr(r), ?(#x(x), ?(#tr(l), ts))) - }; - - switch direction { - case (#fwd) IterSet(s, turnLeftFirst); - case (#bwd) IterSet(s, turnRightFirst) - } - }; - - public func foldLeft( - tree : Tree, - base : Accum, - combine : (Accum, T) -> Accum - ) : Accum { - switch (tree) { - case (#leaf) { base }; - case (#black(l, x, r)) { - let left = foldLeft(l, base, combine); - let middle = combine(left, x); - foldLeft(r, middle, combine) - }; - case (#red(l, x, r)) { - let left = foldLeft(l, base, combine); - let middle = combine(left, x); - foldLeft(r, middle, combine) - } - } - }; - - public func foldRight( - tree : Tree, - base : Accum, - combine : (T, Accum) -> Accum - ) : Accum { - switch (tree) { - case (#leaf) { base }; - case (#black(l, x, r)) { - let right = foldRight(r, base, combine); - let middle = combine(x, right); - foldRight(l, middle, combine) - }; - case (#red(l, x, r)) { - let right = foldRight(r, base, combine); - let middle = combine(x, right); - foldRight(l, middle, combine) - } - } - }; - - func redden(t : Tree) : Tree { - switch t { - case (#black(l, x, r)) { (#red(l, x, r)) }; - case _ { - Runtime.trap "pure/Set.redden() impossible" - } - } - }; - - func lbalance(left : Tree, x : T, right : Tree) : Tree { - switch (left, right) { - case (#red(#red(l1, x1, r1), x2, r2), r) { - #red( - #black(l1, x1, r1), - x2, - #black(r2, x, r) - ) - }; - case (#red(l1, x1, #red(l2, x2, r2)), r) { - #red( - #black(l1, x1, l2), - x2, - #black(r2, x, r) - ) - }; - case _ { - #black(left, x, right) - } - } - }; - - func rbalance(left : Tree, x : T, right : Tree) : Tree { - switch (left, right) { - case (l, #red(l1, x1, #red(l2, x2, r2))) { - #red( - #black(l, x, l1), - x1, - #black(l2, x2, r2) - ) - }; - case (l, #red(#red(l1, x1, r1), x2, r2)) { - #red( - #black(l, x, l1), - x1, - #black(r1, x2, r2) - ) - }; - case _ { - #black(left, x, right) - } - } - }; - - public func add( - set : Set, - compare : (T, T) -> Order.Order, - elem : T - ) : Set { - insert(set, compare, elem).0 - }; - - public func insert( - s : Set, - compare : (T, T) -> Order.Order, - elem : T - ) : (Set, Bool) { - var newNodeIsCreated : Bool = false; - func ins(tree : Tree) : Tree { - switch tree { - case (#black(left, x, right)) { - switch (compare(elem, x)) { - case (#less) { - lbalance(ins left, x, right) - }; - case (#greater) { - rbalance(left, x, ins right) - }; - case (#equal) { - #black(left, x, right) - } - } - }; - case (#red(left, x, right)) { - switch (compare(elem, x)) { - case (#less) { - #red(ins left, x, right) - }; - case (#greater) { - #red(left, x, ins right) - }; - case (#equal) { - #red(left, x, right) - } - } - }; - case (#leaf) { - newNodeIsCreated := true; - #red(#leaf, elem, #leaf) - } - } - }; - let newRoot = switch (ins(s.root)) { - case (#red(left, x, right)) { - #black(left, x, right) - }; - case other { other } - }; - if newNodeIsCreated ({ root = newRoot; size = s.size + 1 }, true) else (s, false) - }; - - func balLeft(left : Tree, x : T, right : Tree) : Tree { - switch (left, right) { - case (#red(l1, x1, r1), r) { - #red(#black(l1, x1, r1), x, r) - }; - case (_, #black(l2, x2, r2)) { - rbalance(left, x, #red(l2, x2, r2)) - }; - case (_, #red(#black(l2, x2, r2), x3, r3)) { - #red( - #black(left, x, l2), - x2, - rbalance(r2, x3, redden r3) - ) - }; - case _ { Runtime.trap "pure/Set.balLeft() impossible" } - } - }; - - func balRight(left : Tree, x : T, right : Tree) : Tree { - switch (left, right) { - case (l, #red(l1, x1, r1)) { - #red(l, x, #black(l1, x1, r1)) - }; - case (#black(l1, x1, r1), r) { - lbalance(#red(l1, x1, r1), x, r) - }; - case (#red(l1, x1, #black(l2, x2, r2)), r3) { - #red( - lbalance(redden l1, x1, l2), - x2, - #black(r2, x, r3) - ) - }; - case _ { Runtime.trap "pure/Set.balRight() impossible" } - } - }; - - func append(left : Tree, right : Tree) : Tree { - switch (left, right) { - case (#leaf, _) { right }; - case (_, #leaf) { left }; - case ( - #red(l1, x1, r1), - #red(l2, x2, r2) - ) { - switch (append(r1, l2)) { - case (#red(l3, x3, r3)) { - #red( - #red(l1, x1, l3), - x3, - #red(r3, x2, r2) - ) - }; - case r1l2 { - #red(l1, x1, #red(r1l2, x2, r2)) - } - } - }; - case (t1, #red(l2, x2, r2)) { - #red(append(t1, l2), x2, r2) - }; - case (#red(l1, x1, r1), t2) { - #red(l1, x1, append(r1, t2)) - }; - case (#black(l1, x1, r1), #black(l2, x2, r2)) { - switch (append(r1, l2)) { - case (#red(l3, x3, r3)) { - #red( - #black(l1, x1, l3), - x3, - #black(r3, x2, r2) - ) - }; - case r1l2 { - balLeft( - l1, - x1, - #black(r1l2, x2, r2) - ) - } - } - } - } - }; - - public func remove(set : Set, compare : (T, T) -> Order.Order, elem : T) : Set { - delete(set, compare, elem).0 - }; - - public func delete(s : Set, compare : (T, T) -> Order.Order, x : T) : (Set, Bool) { - var changed : Bool = false; - func delNode(left : Tree, x1 : T, right : Tree) : Tree { - switch (compare(x, x1)) { - case (#less) { - let newLeft = del left; - switch left { - case (#black(_, _, _)) { - balLeft(newLeft, x1, right) - }; - case _ { - #red(newLeft, x1, right) - } - } - }; - case (#greater) { - let newRight = del right; - switch right { - case (#black(_, _, _)) { - balRight(left, x1, newRight) - }; - case _ { - #red(left, x1, newRight) - } - } - }; - case (#equal) { - changed := true; - append(left, right) - } - } - }; - func del(tree : Tree) : Tree { - switch tree { - case (#black(left, x1, right)) { - delNode(left, x1, right) - }; - case (#red(left, x1, right)) { - delNode(left, x1, right) - }; - case (#leaf) { - tree - } - } - }; - let newRoot = switch (del(s.root)) { - case (#red(left, x1, right)) { - #black(left, x1, right) - }; - case other { other } - }; - if changed ({ root = newRoot; size = s.size - 1 }, true) else (s, false) - }; - - // check binary search tree order of elements and black depth invariant of the RB-tree - public func assertValid(s : Set, comp : (T, T) -> Order.Order) { - ignore blackDepth(s.root, comp) - }; - - func blackDepth(node : Tree, comp : (T, T) -> Order.Order) : Nat { - func checkNode(left : Tree, x1 : T, right : Tree) : Nat { - checkElem(left, func(x : T) : Bool { comp(x, x1) == #less }); - checkElem(right, func(x : T) : Bool { comp(x, x1) == #greater }); - let leftBlacks = blackDepth(left, comp); - let rightBlacks = blackDepth(right, comp); - assert (leftBlacks == rightBlacks); - leftBlacks - }; - switch node { - case (#leaf) 0; - case (#red(left, x1, right)) { - assert (not isRed(left)); - assert (not isRed(right)); - checkNode(left, x1, right) - }; - case (#black(left, x1, right)) { - checkNode(left, x1, right) + 1 - } - } - }; - - func isRed(node : Tree) : Bool { - switch node { - case (#red(_, _, _)) true; - case _ false - } - }; - - func checkElem(node : Tree, isValid : T -> Bool) { - switch node { - case (#leaf) {}; - case (#black(_, elem, _)) { - assert (isValid(elem)) - }; - case (#red(_, elem, _)) { - assert (isValid(elem)) - } - } - } - }; - -} diff --git a/.mops/identity-attributes@0.4.1/LICENSE b/.mops/identity-attributes@0.4.1/LICENSE deleted file mode 100644 index f593a1f..0000000 --- a/.mops/identity-attributes@0.4.1/LICENSE +++ /dev/null @@ -1,208 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, and - distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by the - copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all other - entities that control, are controlled by, or are under common control with - that entity. For the purposes of this definition, "control" means (i) the - power, direct or indirect, to cause the direction or management of such - entity, whether by contract or otherwise, or (ii) ownership of fifty percent - (50%) or more of the outstanding shares, or (iii) beneficial ownership of - such entity. - - "You" (or "Your") shall mean an individual or Legal Entity exercising - permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation source, and - configuration files. - - "Object" form shall mean any form resulting from mechanical transformation - or translation of a Source form, including but not limited to compiled - object code, generated documentation, and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or Object form, - made available under the License, as indicated by a copyright notice that is - included in or attached to the work (an example is provided in the Appendix - below). - - "Derivative Works" shall mean any work, whether in Source or Object form, - that is based on (or derived from) the Work and for which the editorial - revisions, annotations, elaborations, or other modifications represent, as a - whole, an original work of authorship. For the purposes of this License, - Derivative Works shall not include works that remain separable from, or - merely link (or bind by name) to the interfaces of, the Work and Derivative - Works thereof. - - "Contribution" shall mean any work of authorship, including the original - version of the Work and any modifications or additions to that Work or - Derivative Works thereof, that is intentionally submitted to Licensor for - inclusion in the Work by the copyright owner or by an individual or Legal - Entity authorized to submit on behalf of the copyright owner. For the - purposes of this definition, "submitted" means any form of electronic, - verbal, or written communication sent to the Licensor or its - representatives, including but not limited to communication on electronic - mailing lists, source code control systems, and issue tracking systems that - are managed by, or on behalf of, the Licensor for the purpose of discussing - and improving the Work, but excluding communication that is conspicuously - marked or otherwise designated in writing by the copyright owner as "Not a - Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity on - behalf of whom a Contribution has been received by Licensor and subsequently - incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this - License, each Contributor hereby grants to You a perpetual, worldwide, - non-exclusive, no-charge, royalty-free, irrevocable copyright license to - reproduce, prepare Derivative Works of, publicly display, publicly perform, - sublicense, and distribute the Work and such Derivative Works in Source or - Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this - License, each Contributor hereby grants to You a perpetual, worldwide, - non-exclusive, no-charge, royalty-free, irrevocable (except as stated in - this section) patent license to make, have made, use, offer to sell, sell, - import, and otherwise transfer the Work, where such license applies only to - those patent claims licensable by such Contributor that are necessarily - infringed by their Contribution(s) alone or by combination of their - Contribution(s) with the Work to which such Contribution(s) was submitted. - If You institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work or a - Contribution incorporated within the Work constitutes direct or contributory - patent infringement, then any patent licenses granted to You under this - License for that Work shall terminate as of the date such litigation is - filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or - Derivative Works thereof in any medium, with or without modifications, and - in Source or Object form, provided that You meet the following conditions: - - a. You must give any other recipients of the Work or Derivative Works a - copy of this License; and - - b. You must cause any modified files to carry prominent notices stating - that You changed the files; and - - c. You must retain, in the Source form of any Derivative Works that You - distribute, all copyright, patent, trademark, and attribution notices - from the Source form of the Work, excluding those notices that do not - pertain to any part of the Derivative Works; and - - d. If the Work includes a "NOTICE" text file as part of its distribution, - then any Derivative Works that You distribute must include a readable - copy of the attribution notices contained within such NOTICE file, - excluding those notices that do not pertain to any part of the Derivative - Works, in at least one of the following places: within a NOTICE text file - distributed as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, within a - display generated by the Derivative Works, if and wherever such - third-party notices normally appear. The contents of the NOTICE file are - for informational purposes only and do not modify the License. You may - add Your own attribution notices within Derivative Works that You - distribute, alongside or as an addendum to the NOTICE text from the Work, - provided that such additional attribution notices cannot be construed as - modifying the License. - - You may add Your own copyright statement to Your modifications and may - provide additional or different license terms and conditions for use, - reproduction, or distribution of Your modifications, or for any such - Derivative Works as a whole, provided Your use, reproduction, and - distribution of the Work otherwise complies with the conditions stated in - this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any - Contribution intentionally submitted for inclusion in the Work by You to the - Licensor shall be under the terms and conditions of this License, without - any additional terms or conditions. Notwithstanding the above, nothing - herein shall supersede or modify the terms of any separate license agreement - you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, - trademarks, service marks, or product names of the Licensor, except as - required for reasonable and customary use in describing the origin of the - Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in - writing, Licensor provides the Work (and each Contributor provides its - Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied, including, without limitation, any - warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or - FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining - the appropriateness of using or redistributing the Work and assume any risks - associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in - tort (including negligence), contract, or otherwise, unless required by - applicable law (such as deliberate and grossly negligent acts) or agreed to - in writing, shall any Contributor be liable to You for damages, including - any direct, indirect, special, incidental, or consequential damages of any - character arising as a result of this License or out of the use or inability - to use the Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all other - commercial damages or losses), even if such Contributor has been advised of - the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or - Derivative Works thereof, You may choose to offer, and charge a fee for, - acceptance of support, warranty, indemnity, or other liability obligations - and/or rights consistent with this License. However, in accepting such - obligations, You may act only on Your own behalf and on Your sole - responsibility, not on behalf of any other Contributor, and only if You - agree to indemnify, defend, and hold each Contributor harmless for any - liability incurred by, or claims asserted against, such Contributor by - reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -LLVM EXCEPTIONS TO THE APACHE 2.0 LICENSE - -As an exception, if, as a result of your compiling your source code, portions -of this Software are embedded into an Object form of such source code, you may -redistribute such embedded portions in such Object form without complying with -the conditions of Sections 4(a), 4(b) and 4(d) of the License. - -In addition, if you combine or link compiled forms of this Software with -software that is licensed under the GPLv2 ("Combined Software") and if a court -of competent jurisdiction determines that the patent provision (Section 3), the -indemnity provision (Section 9) or other Section of the License conflicts with -the conditions of the GPLv2, you may retroactively and prospectively choose to -deem waived or otherwise exclude such Section(s) of the License, but only in -their entirety and only with respect to the Combined Software. - -END OF LLVM EXCEPTIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification -within third-party archives. - -Copyright 2025 DFINITY Stiftung - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. - -END OF APPENDIX diff --git a/.mops/identity-attributes@0.4.1/NOTICE b/.mops/identity-attributes@0.4.1/NOTICE deleted file mode 100644 index 8874f8c..0000000 --- a/.mops/identity-attributes@0.4.1/NOTICE +++ /dev/null @@ -1,12 +0,0 @@ -Copyright 2026 DFINITY Stiftung - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. diff --git a/.mops/identity-attributes@0.4.1/README.md b/.mops/identity-attributes@0.4.1/README.md deleted file mode 100644 index cbaf4b4..0000000 --- a/.mops/identity-attributes@0.4.1/README.md +++ /dev/null @@ -1,165 +0,0 @@ -# identity-attributes - -Verify Internet Identity attribute bundles in relying-party canisters. -Pairs with `@icp-sdk/auth` v7. - -## Install - -```toml -# mops.toml -[dependencies] -identity-attributes = "0.4.1" -core = "2.5.0" -``` - -Set the canister's environment variables in `icp.yaml`: - -```yaml -canisters: - - name: backend - settings: - environment_variables: - trusted_attribute_signers: "rdmx6-jaaaa-aaaaa-aaadq-cai" # II backend principal (required) - frontend_origins: "https://your-app.icp0.io" # allowed origins, comma-separated (required) - trusted_sso_domains: "dfinity.org" # comma-separated, optional (omit to reject all sso:* keys) -``` - -## Backend - -Add the mixin to your `persistent actor` with `include`. It injects the -two sign-in methods the frontend calls and runs your `onVerified` -callback on each verified bundle. What the callback does is yours to -decide. The example below keeps a profile per principal. - -```motoko -import IdentityAttributes "mo:identity-attributes"; -import Map "mo:core/Map"; -import Principal "mo:core/Principal"; - -persistent actor { - type Profile = { name : ?Text; email : ?Text; sso : ?Text }; - - let profiles = Map.empty(); - - include IdentityAttributes({ - onVerified = func(caller, attrs) { - let profile : Profile = { name = attrs.name; email = attrs.email; sso = attrs.sso }; - profiles.add(caller, profile) - }; - }); - - public query func getProfile(caller : Principal) : async ?Profile { - profiles.get(caller) - }; -}; -``` - -## Frontend - -Fetch a nonce, request the bundle, replay it wrapped in an -`AttributesIdentity`. Passing the nonce as a promise lets sign-in and the -attribute request run together, so the user sees a single II prompt. - -```typescript -import { AuthClient } from "@icp-sdk/auth/client"; -import { AttributesIdentity } from "@icp-sdk/core/identity"; -import { HttpAgent, Actor } from "@icp-sdk/core/agent"; -import { Principal } from "@icp-sdk/core/principal"; - -const authClient = new AuthClient(); - -// Anonymous handle, used only to fetch the nonce. -const anonymousAgent = await HttpAgent.create(); -const anonymousActor = Actor.createActor(idl, { agent: anonymousAgent, canisterId }); - -// Nonce, sign-in, and attributes run in parallel. -const noncePromise = anonymousActor._internet_identity_sign_in_start(); -const signInPromise = authClient.signIn(); -const attributesPromise = authClient.requestAttributes({ - keys: ["name", "verified_email"], // see "Requesting keys" below - nonce: noncePromise, -}); - -const identity = await signInPromise; -const attributes = await attributesPromise; - -// Wrap so the bundle travels as sender_info (signer is the trusted II canister). -const verifiedAgent = await HttpAgent.create({ - identity: new AttributesIdentity({ - inner: identity, - attributes, - signer: { canisterId: Principal.fromText("rdmx6-jaaaa-aaaaa-aaadq-cai") }, - }), -}); -const verifiedActor = Actor.createActor(idl, { agent: verifiedAgent, canisterId }); - -const result = await verifiedActor._internet_identity_sign_in_finish(); // #ok once onVerified has run -``` - -### Requesting keys - -The `keys` array lists the II attribute keys to request. This library reads two of them: - -- **name**: `name`, `openid::name`, or `sso::name` -- **email**: `verified_email`, `openid::verified_email`, or `sso::email` - -Where: - -- `` is `https://accounts.google.com`, `https://appleid.apple.com`, or `https://login.microsoftonline.com/{tid}/v2.0` (`{tid}` is literal). -- `` is one of `trusted_sso_domains`. - -Use `scopedKeys` from `@icp-sdk/auth/client` to build the scoped key -forms. For example, scoping to Google: - -```typescript -scopedKeys({ openIdProvider: "google", keys: ["name", "verified_email"] }) -// returns ["openid:https://accounts.google.com:name", -// "openid:https://accounts.google.com:verified_email"] -``` - -## API - -```motoko -include IdentityAttributes({ - onVerified : (Principal, { name : ?Text; email : ?Text; sso : ?Text }) -> () -}); - -// Injected on your actor: -_internet_identity_sign_in_start() : async Blob -_internet_identity_sign_in_finish() : async Result<(), IdentityAttributesError> - -type IdentityAttributesError = { - #NoAttributes; - #MalformedCandid; - #MissingField : Text; - #FrontendOriginsNotConfigured; - #FrontendOriginMismatch : { expected : [Text]; got : Text }; - #Stale : { ageNs : Nat }; - #UnknownNonce; - #AmbiguousAttribute : { field : Text; sources : [Text] }; - #UntrustedSsoSource : { domain : Text }; - #MixedSsoSources : { ssoKeys : [Text]; otherKeys : [Text] }; -}; -``` - -Your `onVerified` callback receives the caller and the resolved -`{ name; email; sso }`. The `sso` field is the matched domain when -name/email came from `sso:` keys, otherwise `null`. - -Resolution rules: - -- Each field resolves from at most one key. Two candidates returns `#AmbiguousAttribute`. -- A bundle may carry both non-SSO and SSO keys, but the two are never combined: a mixed bundle is rejected with `#MixedSsoSources`. -- An untrusted `sso::*` key rejects the whole bundle with `#UntrustedSsoSource`. -- Non-SSO email comes from `verified_email`. SSO email comes from `sso::email`. - -## Compatibility - -| `mo:identity-attributes` | `@icp-sdk/auth` | -|---|---| -| `^0.4` | `^7` | -| `^0.3` | `^7` | - -## License - -Apache-2.0. diff --git a/.mops/identity-attributes@0.4.1/mops.toml b/.mops/identity-attributes@0.4.1/mops.toml deleted file mode 100644 index 24cbbff..0000000 --- a/.mops/identity-attributes@0.4.1/mops.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "identity-attributes" -version = "0.4.1" -description = "Higher-level helpers for consuming Internet Identity certified attributes in Motoko canisters." -keywords = [ - "internet-identity", - "icrc-3", - "identity", - "attributes", - "icp" -] -license = "Apache-2.0" -readme = "README.md" -repository = "https://github.com/dfinity/motoko-identity-attributes" - -[dependencies] -core = "2.5.0" - -[toolchain] -moc = "1.6.0" diff --git a/.mops/identity-attributes@0.4.1/src/Internal/Attributes.mo b/.mops/identity-attributes@0.4.1/src/Internal/Attributes.mo deleted file mode 100644 index dde093c..0000000 --- a/.mops/identity-attributes@0.4.1/src/Internal/Attributes.mo +++ /dev/null @@ -1,286 +0,0 @@ -import Value "./Value"; -import Result "mo:core/Result"; -import Array "mo:core/Array"; -import Text "mo:core/Text"; -import Iter "mo:core/Iter"; - -/// Decoded attribute bundle and the typed view consumers see. -/// -/// `Attributes` is the bundle as an opaque internal class — used by -/// `Verify` to read `implicit:*` fields. It is not re-exported from -/// `lib.mo`; consumers only see the typed `IdentityAttributes`. -/// -/// `IdentityAttributes` is the typed result of `Verify.verify`: a -/// single `name`, a single `email`, and an optional `sso` domain. -/// `name` and `email` are each sourced from at most one key in the -/// bundle, drawn from a single category: -/// -/// - **unscoped/openid** — `name` / `verified_email` or -/// `openid::name` / `openid::verified_email`. -/// - **sso** — `sso::name` / `sso::email`, where -/// `` is one of the canister's `trusted_sso_domains`. -/// -/// The two categories can never mix in a single bundle. Mixing yields -/// `#MixedSsoSources`. An `sso::*` key whose domain isn't -/// trusted rejects the bundle with `#UntrustedSsoSource` even if the -/// rest of the bundle is well-formed. -module { - - type Value = Value.Value; - - /// Decoded attribute bundle. Internal — used by `Verify` for the - /// `implicit:*` reads. Not exposed to consumers. - public class Attributes(initialEntries : [(Text, Value)]) { - - let entries = initialEntries; - - /// All entries — used by `asIdentityAttributes` to walk the bundle - /// looking for `sso::*` keys whose domains aren't known - /// ahead of time. - public func all() : [(Text, Value)] = entries; - - /// Whether `key` is present in the bundle, regardless of its value type. - public func has(key : Text) : Bool { - for ((entryKey, _) in entries.vals()) { if (entryKey == key) return true }; - false - }; - - /// Exact-match lookup for a `Text`-valued entry. Returns `null` if - /// the key is missing OR if the entry exists but isn't `Text`. - public func getText(key : Text) : ?Text { - for ((entryKey, value) in entries.vals()) { - if (entryKey == key) { - switch value { case (#Text text) return ?text; case _ {} } - } - }; - null - }; - - /// Exact-match lookup for a `Nat`-valued entry. - public func getNat(key : Text) : ?Nat { - for ((entryKey, value) in entries.vals()) { - if (entryKey == key) { - switch value { case (#Nat nat) return ?nat; case _ {} } - } - }; - null - }; - - /// Exact-match lookup for a `Blob`-valued entry. - public func getBlob(key : Text) : ?Blob { - for ((entryKey, value) in entries.vals()) { - if (entryKey == key) { - switch value { case (#Blob blob) return ?blob; case _ {} } - } - }; - null - } - }; - - /// What `Verify.verify` hands back on success. - /// - /// `name` and `email` are sourced from a single matching key in the - /// bundle. `sso` is the matched SSO domain when the bundle's - /// name/email came from `sso::*` keys, otherwise `null`. - /// - /// **`email` semantics differ by category.** For unscoped and - /// openid sources, only `verified_email`-suffixed keys count — the - /// unverified `email` key is user-supplied and never lands here. - /// For SSO sources the key is literally `sso::email`: the - /// IdP behind `` attests the value, so there is no separate - /// verification flag. The email's own domain may be anything. - public type IdentityAttributes = { - name : ?Text; - email : ?Text; - sso : ?Text - }; - - /// A single logical field has more than one source in the bundle. - /// `sources` lists the conflicting keys. - public type AmbiguousAttribute = { - field : Text; - sources : [Text] - }; - - /// All ways `asIdentityAttributes` can reject the bundle. - public type Error = { - /// Two or more keys populate the same logical field (`name`, - /// `email`, or `sso` when SSO sources span multiple domains). - #AmbiguousAttribute : AmbiguousAttribute; - /// The bundle contains an `sso::*` key whose `` - /// is not listed in `trusted_sso_domains`. The whole bundle is - /// rejected — we don't silently strip untrusted SSO claims. - #UntrustedSsoSource : { domain : Text }; - /// The bundle mixes SSO and non-SSO sources for name/email. - /// Either the unscoped/openid keys are present alongside SSO - /// keys, or vice versa. `ssoKeys` and `otherKeys` list the - /// offending entries. - #MixedSsoSources : { ssoKeys : [Text]; otherKeys : [Text] } - }; - - /// Construct an `Attributes` from a decoded top-level `#Map`. Returns - /// `null` if the value isn't a map. - public func fromValue(value : Value) : ?Attributes { - switch value { case (#Map entries) ?Attributes(entries); case _ null } - }; - - // OpenID provider prefixes plus the empty unscoped prefix. `{tid}` in - // the Microsoft URL is a *literal* part of the key Internet Identity - // emits, not a placeholder for a tenant GUID. - let openidPrefixes : [Text] = [ - "", - "openid:https://accounts.google.com:", - "openid:https://appleid.apple.com:", - "openid:https://login.microsoftonline.com/{tid}/v2.0:" - ]; - - // Parse a key shaped `sso::`. Returns null if the - // key doesn't have exactly three colon-separated parts or the first - // part isn't `sso`. Email domains don't contain colons in practice, - // so the three-part split is unambiguous. - func parseSsoKey(key : Text) : ?(Text, Text) { - let parts = Iter.toArray(Text.split(key, #char ':')); - if (parts.size() != 3) return null; - if (parts[0] != "sso") return null; - ?(parts[1], parts[2]) - }; - - // Resolve one field across the unscoped/openid prefixes. Returns - // null/one/error mirroring the pre-SSO behavior. - func resolveNonSsoField(attributes : Attributes, field : Text, suffix : Text) : Result.Result { - var value : ?Text = null; - var sources : [Text] = []; - for (prefix in openidPrefixes.vals()) { - let key = prefix # suffix; - switch (attributes.getText(key)) { - case null {}; - case (?v) { - value := ?v; - sources := Array.concat(sources, [key]) - } - } - }; - if (sources.size() > 1) #err({ field; sources }) else #ok(value) - }; - - // True iff the bundle has at least one non-SSO name/email source. - func hasNonSsoNameOrEmail(attributes : Attributes) : [Text] { - var keys : [Text] = []; - for (prefix in openidPrefixes.vals()) { - for (suffix in (["name", "verified_email"] : [Text]).vals()) { - let key = prefix # suffix; - if (attributes.has(key)) keys := Array.concat(keys, [key]) - } - }; - keys - }; - - /// Populate `IdentityAttributes` from a decoded bundle. - /// - /// `trustedSsoDomains` is the canister's `trusted_sso_domains` env - /// var, parsed. An empty list means "this canister doesn't accept - /// SSO sources" — any `sso:*` key in the bundle rejects it via - /// `#UntrustedSsoSource`. - public func asIdentityAttributes( - attributes : Attributes, - trustedSsoDomains : [Text] - ) : Result.Result { - - // Scan for sso:: keys, separating trusted from - // untrusted and name from email. Any untrusted SSO source rejects - // the bundle outright. - var untrustedSsoDomain : ?Text = null; - var ssoNameSources : [(Text, Text, Text)] = []; // (domain, key, value) - var ssoEmailSources : [(Text, Text, Text)] = []; - - for ((key, value) in attributes.all().vals()) { - switch (parseSsoKey(key)) { - case null {}; - case (?(domain, suffix)) { - switch value { - case (#Text v) { - if (Array.find(trustedSsoDomains, func d = d == domain) == null) { - if (untrustedSsoDomain == null) untrustedSsoDomain := ?domain - } else if (suffix == "name") { - ssoNameSources := Array.concat<(Text, Text, Text)>(ssoNameSources, [(domain, key, v)]) - } else if (suffix == "email") { - ssoEmailSources := Array.concat<(Text, Text, Text)>(ssoEmailSources, [(domain, key, v)]) - } - }; - case _ {} - } - } - } - }; - - switch (untrustedSsoDomain) { - case (?d) return #err(#UntrustedSsoSource { domain = d }); - case null {} - }; - - let hasSso = ssoNameSources.size() > 0 or ssoEmailSources.size() > 0; - - if (hasSso) { - // The bundle is SSO-flavored. Reject if any non-SSO name/email - // source is also present — mixing the two categories is never - // allowed (it would let an attacker who controls one IdP shadow - // another). - let otherKeys = hasNonSsoNameOrEmail(attributes); - if (otherKeys.size() > 0) { - var ssoKeys : [Text] = []; - for ((_, k, _) in ssoNameSources.vals()) ssoKeys := Array.concat(ssoKeys, [k]); - for ((_, k, _) in ssoEmailSources.vals()) ssoKeys := Array.concat(ssoKeys, [k]); - return #err(#MixedSsoSources { ssoKeys; otherKeys }) - }; - - // All SSO sources must share one domain. If name comes from - // dfinity.org and email from acme.com, the bundle is asking us - // to splice claims from two IdPs — reject. - var domain : ?Text = null; - var domainSources : [Text] = []; - for (src in ssoNameSources.vals()) { - domainSources := Array.concat(domainSources, [src.1]); - switch (domain) { - case null { domain := ?src.0 }; - case (?d0) if (src.0 != d0) return #err(#AmbiguousAttribute { field = "sso"; sources = domainSources }) - } - }; - for (src in ssoEmailSources.vals()) { - domainSources := Array.concat(domainSources, [src.1]); - switch (domain) { - case null { domain := ?src.0 }; - case (?d0) if (src.0 != d0) return #err(#AmbiguousAttribute { field = "sso"; sources = domainSources }) - } - }; - - // Within the single domain, name and email each must have ≤ 1 - // source. Two `sso:dfinity.org:name` entries is malformed. - if (ssoNameSources.size() > 1) { - var sources : [Text] = []; - for (src in ssoNameSources.vals()) sources := Array.concat(sources, [src.1]); - return #err(#AmbiguousAttribute { field = "name"; sources }) - }; - if (ssoEmailSources.size() > 1) { - var sources : [Text] = []; - for (src in ssoEmailSources.vals()) sources := Array.concat(sources, [src.1]); - return #err(#AmbiguousAttribute { field = "email"; sources }) - }; - - let nameVal = if (ssoNameSources.size() == 1) ?ssoNameSources[0].2 else null; - let emailVal = if (ssoEmailSources.size() == 1) ?ssoEmailSources[0].2 else null; - return #ok { name = nameVal; email = emailVal; sso = domain } - }; - - // No SSO sources — fall through to the unscoped/openid resolution. - let name = switch (resolveNonSsoField(attributes, "name", "name")) { - case (#err e) return #err(#AmbiguousAttribute e); - case (#ok v) v - }; - let email = switch (resolveNonSsoField(attributes, "email", "verified_email")) { - case (#err e) return #err(#AmbiguousAttribute e); - case (#ok v) v - }; - #ok { name; email; sso = null } - }; - -} diff --git a/.mops/identity-attributes@0.4.1/src/Internal/Challenges.mo b/.mops/identity-attributes@0.4.1/src/Internal/Challenges.mo deleted file mode 100644 index 4e80e40..0000000 --- a/.mops/identity-attributes@0.4.1/src/Internal/Challenges.mo +++ /dev/null @@ -1,127 +0,0 @@ -import Random "mo:core/Random"; -import Map "mo:core/Map"; -import Blob "mo:core/Blob"; -import Time "mo:core/Time"; -import Int "mo:core/Int"; -import Iter "mo:core/Iter"; -import Result "mo:core/Result"; - -/// Single-use canister-issued nonces, held in a `Map` keyed -/// by nonce bytes with the issue timestamp as the value. -/// -/// ## Why nonces are canister-issued -/// -/// Internet Identity's attribute-bundle protocol bakes the nonce into -/// the signed bundle so the relying-party canister can prove "I started -/// this flow". A frontend-generated nonce gives the canister no way to -/// distinguish a fresh user flow from an attacker replaying or -/// laundering an old bundle. Mint here, store here, consume here. -/// -/// ## Pruning -/// -/// `issue` and `consume` both drop entries older than `expiryNs` before -/// touching the store, so memory stays bounded even if no one calls -/// `consume`. On top of that, `issue` evicts the oldest entry when the -/// store is already at `maxTotal` — protects against a runaway frontend -/// minting nonces but never using them. -/// -/// The lib doesn't refuse a `consume` on an expired entry — the entry -/// is gone by then and the call returns `#UnknownNonce`, which is what -/// the consumer wants anyway. The bundle's own -/// `implicit:issued_at_timestamp_ns` freshness check in `Verify` is -/// the authoritative stale-bundle gate. -/// -/// ## Upgrade behavior -/// -/// The store lives inside the `transient` `IdentityAttributesProvider`, -/// so it's recreated empty on every upgrade. In-flight authentications -/// will just need to retry — the `expiryNs` window means anything that -/// would have been redeemable was about to time out anyway. -/// -/// ## Why we don't key by `Principal` -/// -/// In the canonical flow the begin endpoint is called anonymously -/// (before Internet Identity sign-in) and the finish endpoint is called -/// authenticated, so the two callers differ. Cross-user replay is -/// handled by the bundle signature itself: the IC binds the bundle to -/// the caller of the finish endpoint, so an attacker who steals a -/// nonce only manages to register themselves. -module { - - /// Upper bound on store size before `issue` starts evicting. - public let maxTotal : Nat = 4096; - - /// Per-entry lifetime. Matches `Verify`'s bundle freshness window — - /// nothing older than this could be redeemed anyway. - public let expiryNs : Nat = 300_000_000_000; - - public type Store = Map.Map; - - public type ConsumeError = { #UnknownNonce }; - - /// Fresh empty store. Use this when constructing the provider. - public func empty() : Store = Map.empty(); - - /// Mint a fresh 32-byte random nonce, prune expired entries, evict - /// the oldest entry if the store is already at capacity, then add - /// the new nonce with the current timestamp. Returns the nonce. - public func issue(store : Store) : async Blob { - let nonce = await Random.blob(); - let nowNs = Int.abs(Time.now()); - pruneExpired(store, nowNs); - if (Map.size(store) >= maxTotal) { - evictOldest(store) - }; - Map.add(store, Blob.compare, nonce, nowNs); - nonce - }; - - /// Prune expired entries, then look up `nonce` and remove it in one - /// shot. Returns `#err(#UnknownNonce)` if the entry isn't present — - /// either it was never issued by this canister, was already - /// consumed, or expired and got pruned. - public func consume(store : Store, nonce : Blob) : Result.Result<(), ConsumeError> { - let nowNs = Int.abs(Time.now()); - pruneExpired(store, nowNs); - switch (Map.take(store, Blob.compare, nonce)) { - case (?_) #ok; - case null #err(#UnknownNonce) - } - }; - - // Drop every entry whose age exceeds `expiryNs`. Two-pass because - // `mo:core/Map` doesn't expose an in-place filter — collect the - // expired keys first, then remove them. - func pruneExpired(store : Store, nowNs : Int) { - let expired = Iter.toArray( - Iter.map<(Blob, Int), Blob>( - Iter.filter<(Blob, Int)>( - Map.entries(store), - func((_, issuedAt)) = nowNs - issuedAt > expiryNs - ), - func((nonce, _)) = nonce - ) - ); - for (nonce in expired.vals()) { - Map.remove(store, Blob.compare, nonce) - } - }; - - // Remove the entry with the smallest `issuedAt`. Linear scan — only - // happens on `issue` when the store is at `maxTotal`, so the cost is - // bounded by `maxTotal` and only hit in degenerate cases. - func evictOldest(store : Store) { - var oldest : ?(Blob, Int) = null; - for (entry in Map.entries(store)) { - switch oldest { - case null oldest := ?entry; - case (?(_, oldT)) if (entry.1 < oldT) oldest := ?entry - } - }; - switch oldest { - case (?(nonce, _)) Map.remove(store, Blob.compare, nonce); - case null {} - } - }; - -} diff --git a/.mops/identity-attributes@0.4.1/src/Internal/Value.mo b/.mops/identity-attributes@0.4.1/src/Internal/Value.mo deleted file mode 100644 index 2756ccf..0000000 --- a/.mops/identity-attributes@0.4.1/src/Internal/Value.mo +++ /dev/null @@ -1,24 +0,0 @@ -/// ICRC-3 `Value` tree. -/// -/// Mirrors the Candid type Internet Identity uses when it certifies attribute -/// bundles. Internal to the library — the public surface speaks `Verified` -/// and the typed accessors on `Attributes`, not raw `Value`. -/// -/// See: https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md -module { - public type Value = { - #Nat : Nat; - #Int : Int; - #Blob : Blob; - #Text : Text; - #Array : [Value]; - #Map : [(Text, Value)] - }; - - /// Candid-decode an ICRC-3 `Value` blob. Returns `null` if the bytes don't - /// match the expected type. - public func decode(blob : Blob) : ?Value { - let decoded : ?Value = from_candid (blob); - decoded - } -} diff --git a/.mops/identity-attributes@0.4.1/src/Internal/Verify.mo b/.mops/identity-attributes@0.4.1/src/Internal/Verify.mo deleted file mode 100644 index 34596e2..0000000 --- a/.mops/identity-attributes@0.4.1/src/Internal/Verify.mo +++ /dev/null @@ -1,134 +0,0 @@ -import CallerAttributes "mo:core/CallerAttributes"; -import Runtime "mo:core/Runtime"; -import Time "mo:core/Time"; -import Int "mo:core/Int"; -import Result "mo:core/Result"; -import Text "mo:core/Text"; -import Iter "mo:core/Iter"; -import Array "mo:core/Array"; -import Value "./Value"; -import Attributes "./Attributes"; -import Challenges "./Challenges"; - -/// Walks a single attribute bundle through every invariant the canister -/// can check, *given* that the IC has already enforced "the bundle is -/// signed by someone we trust" via `mo:core/CallerAttributes`. -/// -/// 1. A bundle is actually attached to the call (`#NoAttributes`). -/// 2. The bundle's Candid payload decodes to an ICRC-3 `Value::Map` -/// (`#MalformedCandid`). -/// 3. The `frontend_origins` canister env var is set -/// (`#FrontendOriginsNotConfigured`). -/// 4. `implicit:origin` is one of the configured `frontend_origins`. -/// 5. `implicit:issued_at_timestamp_ns` is within the freshness window. -/// 6. `implicit:nonce` is one this canister issued, not yet consumed. -/// 7. `name`/`email` are sourced uniformly (see `Attributes`). -module { - - public type IdentityAttributes = Attributes.IdentityAttributes; - - public type Error = { - /// No bundle is attached to this call. Either the frontend forgot - /// to wrap the identity with `AttributesIdentity`, or it wrapped - /// against a signer this canister doesn't trust. - #NoAttributes; - /// The bundle is trusted-signed but its payload isn't a well-formed - /// ICRC-3 `Value::Map`. Treat as a protocol mismatch — either Internet Identity - /// has rev'd its wire format and this library is out of date, or - /// someone is hand-crafting garbage payloads. - #MalformedCandid; - /// A required implicit field is missing. - #MissingField : Text; - /// The canister's `frontend_origins` environment variable isn't - /// set, or parses to an empty list. Configure it under - /// `canisters[].settings.environment_variables.frontend_origins` - /// in `icp.yaml`, or set it on a deployed canister with - /// `icp canister settings update --add-environment-variable frontend_origins=[,...]`. - #FrontendOriginsNotConfigured; - /// `implicit:origin` doesn't match any value in `frontend_origins`. - /// Usually means the FE call went to the wrong backend, or someone - /// is trying to launder a bundle minted for a different dapp. - #FrontendOriginMismatch : { expected : [Text]; got : Text }; - /// `implicit:issued_at_timestamp_ns` is older than the freshness - /// window. The FE should fetch a fresh nonce and try again. - #Stale : { ageNs : Nat }; - /// The bundle's nonce was never issued by this canister, or was - /// issued and already consumed. Stale-but-stored nonces are caught - /// by `#Stale` (the bundle freshness check) before we get here. - #UnknownNonce; - /// A logical field on `IdentityAttributes` (`"name"`, `"email"`, - /// or `"sso"` when name+email come from different SSO domains) is - /// sourced from more than one key in the bundle. - #AmbiguousAttribute : { field : Text; sources : [Text] }; - /// The bundle contains an `sso::*` key whose `` - /// is not listed in `trusted_sso_domains`. The whole bundle is - /// rejected — we don't silently strip untrusted SSO claims. - #UntrustedSsoSource : { domain : Text }; - /// The bundle mixes SSO and non-SSO sources for name/email. A - /// bundle is either fully SSO (all keys `sso::*`, - /// same domain) or fully non-SSO (unscoped/openid). `ssoKeys` and - /// `otherKeys` list the offending entries. - #MixedSsoSources : { ssoKeys : [Text]; otherKeys : [Text] } - }; - - /// Five minutes in nanoseconds. Applied to the bundle's - /// `implicit:issued_at_timestamp_ns` freshness check. - let maxAgeNs : Nat = 300_000_000_000; - - // Parse a comma-separated env var value. Empty entries are dropped, - // so trailing commas and accidental whitespace-only entries don't - // turn into bogus list members. Surrounding whitespace on each entry - // is left intact — env values are operator-controlled and we'd - // rather mismatch loudly than silently normalize a typo. - func parseList(raw : Text) : [Text] { - let parts = Iter.toArray(Text.split(raw, #char ',')); - Array.filter(parts, func t = Text.size(t) > 0) - }; - - public func verify(store : Challenges.Store) : Result.Result { - - let ?rawBundle = CallerAttributes.getAttributes() else return #err(#NoAttributes); - let ?decoded = Value.decode(rawBundle) else return #err(#MalformedCandid); - let ?attrs = Attributes.fromValue(decoded) else return #err(#MalformedCandid); - - let nowNs = Int.abs(Time.now()); - - let ?rawFrontendOrigins = Runtime.envVar("frontend_origins") else return #err(#FrontendOriginsNotConfigured); - let frontendOrigins = parseList(rawFrontendOrigins); - if (frontendOrigins.size() == 0) return #err(#FrontendOriginsNotConfigured); - - let ?gotOrigin = attrs.getText("implicit:origin") else return #err(#MissingField "implicit:origin"); - if (Array.find(frontendOrigins, func o = o == gotOrigin) == null) { - return #err(#FrontendOriginMismatch { expected = frontendOrigins; got = gotOrigin }) - }; - - let ?issuedAt = attrs.getNat("implicit:issued_at_timestamp_ns") else return #err(#MissingField "implicit:issued_at_timestamp_ns"); - if (nowNs >= issuedAt) { - let age = nowNs - issuedAt : Nat; - if (age > maxAgeNs) return #err(#Stale { ageNs = age }) - }; - - let ?bundleNonce = attrs.getBlob("implicit:nonce") else return #err(#MissingField "implicit:nonce"); - switch (Challenges.consume(store, bundleNonce)) { - case (#err(#UnknownNonce)) return #err(#UnknownNonce); - case (#ok) {} - }; - - // Optional — when unset, asIdentityAttributes treats every sso:* - // key in the bundle as untrusted, which surfaces the bundle as - // #UntrustedSsoSource. This is the safe default: a canister - // author opts in to SSO domains explicitly. - let trustedSsoDomains = switch (Runtime.envVar("trusted_sso_domains")) { - case null []; - case (?raw) parseList(raw) - }; - - switch (Attributes.asIdentityAttributes(attrs, trustedSsoDomains)) { - case (#err(#AmbiguousAttribute e)) #err(#AmbiguousAttribute e); - case (#err(#UntrustedSsoSource e)) #err(#UntrustedSsoSource e); - case (#err(#MixedSsoSources e)) #err(#MixedSsoSources e); - case (#ok r) #ok r - } - }; - -} diff --git a/.mops/identity-attributes@0.4.1/src/lib.mo b/.mops/identity-attributes@0.4.1/src/lib.mo deleted file mode 100644 index 30548cb..0000000 --- a/.mops/identity-attributes@0.4.1/src/lib.mo +++ /dev/null @@ -1,60 +0,0 @@ -import Challenges "./Internal/Challenges"; -import Verify "./Internal/Verify"; -import Principal "mo:core/Principal"; -import Result "mo:core/Result"; - -/// Mixin that injects the two canister methods needed to verify -/// Internet Identity attribute bundles into your actor. Pairs with -/// `@icp-sdk/auth` v7's `requestAttributes` / `AttributesIdentity` flow. -/// -/// Usage: -/// -/// ```motoko -/// import IdentityAttributes "mo:identity-attributes"; -/// -/// persistent actor { -/// include IdentityAttributes({ -/// onVerified = func(caller, attrs) { -/// // persist `attrs` for `caller` however your app needs -/// }; -/// }); -/// }; -/// ``` -/// -/// Injected methods: -/// - `_internet_identity_sign_in_start() : async Blob`. The frontend -/// calls this anonymously before sign-in to get a fresh nonce. -/// - `_internet_identity_sign_in_finish() : async Result<(), IdentityAttributesError>`. -/// The frontend calls this after sign-in, wrapped in an -/// `AttributesIdentity`. On success, `config.onVerified(caller, attrs)` -/// runs with the verified principal and `{ name; email; sso }`. The -/// `sso` field is the matched trusted SSO domain when the bundle's -/// name/email came from `sso::*` keys, otherwise `null`. -/// -/// The nonce store lives inside the mixin as a `transient` field. -/// Motoko's `persistent actor` requires class-like state to be -/// transient, so the store is recreated empty on every upgrade. -/// In-flight authentications will retry. Nothing older than the -/// 5-minute freshness window would have been redeemable anyway. -mixin( - config : { - onVerified : (Principal, { name : ?Text; email : ?Text; sso : ?Text }) -> () - } -) { - - transient let challenges = Challenges.empty(); - - public shared func _internet_identity_sign_in_start() : async Blob { - await Challenges.issue(challenges) - }; - - public shared ({ caller }) func _internet_identity_sign_in_finish() : async Result.Result<(), Verify.Error> { - switch (Verify.verify(challenges)) { - case (#err e) #err e; - case (#ok attrs) { - config.onVerified(caller, attrs); - #ok - } - } - } -} diff --git a/.mops/json@1.4.0/LICENSE b/.mops/json@1.4.0/LICENSE deleted file mode 100644 index 542cea4..0000000 --- a/.mops/json@1.4.0/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 Demali.icp - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/.mops/json@1.4.0/README.md b/.mops/json@1.4.0/README.md deleted file mode 100644 index dee510d..0000000 --- a/.mops/json@1.4.0/README.md +++ /dev/null @@ -1,382 +0,0 @@ -# Motoko JSON Library - -![JSONXMOTOKO](motokoxjson.png) - -A standards-compliant JSON (ECMA-404/RFC 8259) library for the Motoko programming language, providing native JSON manipulation capabilities for Internet Computer applications. - -## Overview - -This library enables developers to: - -1. Parse JSON text into native Motoko data structures -2. Manipulate JSON data directly in Motoko -3. Serialize modified JSON back to standard JSON text - -## Installation - -```bash -mops add json -``` - -## Usage - -```bash -import Json "mo:json"; -import {str; int; float; bool; nullable; obj; arr } "mo:json"; //JSON Types -import {string; number; boolean; nullSchema; array, schemaObject} "mo:json"; //JSON Schema Types -``` - -## Core Types - -```motoko -public type Json = { - #object_ : [(Text, Json)]; - #array : [Json]; - #string : Text; - #number : { - #int : Int; - #float : Float; - }; - #bool : Bool; - #null_; -}; -``` - -## API Reference - -### 1. Parsing JSON - -The `parse` function converts JSON text into Motoko's JSON type: - -```motoko -public func parse(input: Text) : Result.Result -``` - -Example usage: - -```motoko -let jsonText = "{ \"name\": \"John\", \"age\": 30 }"; - -switch(Json.parse(jsonText)) { - case (#ok(parsed)) { - // Work with parsed JSON - }; - case (#err(e)) { - // Handle error - }; -}; -``` - -### 2. Querying JSON (get) - -Retrieve values from JSON using path expressions: - -```motoko -public func get(json: Json.Json, Json.path: Path) : ?Json.Json -``` - -Path syntax: - -- Use dots for object properties: "user.name" -- Use brackets for array indices: "users[0]" -- Use wildcards for multiple matches: "users.\*.name" - -Example: - -```motoko -let data = obj([ - ("users", arr([ - obj([ - ("name", str("John")), - ("age", int(30)) - ]) - ])) -]); - -// Get a specific value -let name = Json.get(data, "users[0].name"); // Returns ?#string("John") -// Or get text value -let nameText = Json.getAsText(data, "users[0].name"); // Returns Result.Result - -// Get multiple values using wildcard -let allNames = Json.get(data, "users.*.name"); // Returns array of all names -``` - -### 3. Modifying JSON (set) - -Add or update values in JSON using path expressions: - -```motoko -public func set(json: Json.Json, path: Json.Path, value: Json.Json) : Json.Json -``` - -Example: - -```motoko -// Add a new field -let withPhone = Json.set(data, "users[0].phone", str("+1234567890")); - -// Update existing value -let updated = Json.set(data, "users[0].age", int(31)); - -// Create nested structure -let nested = Json.set(data, "metadata.lastUpdated", str("2024-01-11")); -``` - -### 4. Removing Data (remove) - -Remove values from JSON using path expressions: - -```motoko -public func remove(json: Json.Json, path: Json.Path) : Json.Json -``` - -Example: - -```motoko -// Remove a field -let withoutEmail = Json.remove(data, "users[0].email"); - -// Remove an array element -let withoutFirstUser = Json.remove(data, "users[0]"); - - -``` - -### 5. Serializing Json (stringify) - -Convert Json back to text with optional transformation: - -```motoko -public type Replacer = { - #function : (Text, Json.Json) -> ?Json.Json; - #keys : [Text]; -}; - -public func stringify(json: Json.Json, replacer: ?Json.Replacer) : Text -``` - -Example: - -```motoko -// Basic stringify -let jsonText = Json.stringify(data, null); - -// With replacer function to hide sensitive data -let replacer = #function(func(key: Text, value: Json.Json) : ?Json.Json { - if (key == "password") { - ?#string("****") - } else { - ?value - } -}); -let safeJson = Json.stringify(data, ?replacer); - -// With key filter to include specific fields -let keys = #keys(["name", "age"]); -let filtered = Json.stringify(data, ?keys); -``` - -## Complete Example - -Here's a full workflow example: - -```motoko -// Start with JSON text -let jsonText = "{ - \"users\": [ - { - \"name\": \"John\", - \"email\": \"john@example.com\", - \"age\": 30 - } - ] -}"; - -// Parse it -switch(Json.parse(jsonText)) { - case (#ok(data)) { - // Get existing data - let name = Json.get(data, "users[0].name"); - - // Add new data - let updated = Json.set(data, "users[0].phone", str("+1234567890")); - - // Remove sensitive data - let cleaned = Json.remove(updated, "users[0].email"); - - // Convert back to JSON text - let finalJson = Json.stringify(cleaned, null); - }; - case (#err(e)) { - Debug.print("Parse error: " # debug_show(e)); - }; -}; -``` - -## 6. Schema Validation - -The library supports JSON Schema validation allowing you to verify JSON data structures match an expected schema: - -```motoko -public func validate(json: Json.Json, schema: Json.Schema) : Result.Result<(), Json.ValidationError> -``` - -Schema Type: - -```motoko -public type Schema = { - #object_ : { - properties : [(Text, Schema)]; - required : ?[Text]; - }; - #array : { - items : Schema; - }; - #string; - #number; - #boolean; - #null; -}; -``` - -Example usage: - -```motoko -// Define a schema -let userSchema = schemaObject([ - ("name", string()), - ("age", number()), - ("tags", array(string())) -], ?["name"]); // name is required - -// Validate instance -switch(Json.validate(myJson, userSchema)) { - case (#ok()) { - // JSON is valid - }; - case (#err(#TypeError{expected; got; path})) { - // Type mismatch error - }; - case (#err(#RequiredField(field))) { - // Missing required field - }; -}; -``` - -## Standard Compliance - -This library strictly follows ECMA-404/RFC 8259: - -- Proper Unicode support -- Complete escape sequence handling -- Strict number format validation -- No trailing commas -- Only double quotes for strings -- No comments - -## Limitations - -This library is in active development feedback and bug reports are welcome. Some important considerations: - -- The `set` method allows creating new paths by default, which might lead to unintended data structure changes. Use with caution and consider validating your JSON structure with schemas before modifications. - -- Schema validation is currently basic the plan is to support the full [JSON Schema specification](https://json-schema.org/) in future releases. - -1. Number Precision - - - Integers are limited to Motoko's Int bounds - - Floats follow IEEE 754 double-precision format - -2. Object Keys - - - Must be strings - - No duplicate keys (last one wins) - -3. Special Values - - JavaScript `undefined` is not supported - - `NaN` and `Infinity` are not valid JSON values - -Please report any issues or suggestions at the GitHub repository. - -## Error Handling - -```motoko -public type Error = { - #invalidString : Text; - #invalidNumber : Text; - #invalidKeyword : Text; - #invalidChar : Text; - #unexpectedEOF; - #unexpectedToken : Text; -}; -``` - -The library provides detailed error information for debugging and validation. - -## Path Expressions - -The library uses a simple and intuitive path syntax for accessing and modifying JSON data: - -```motoko -// Basic property access -"user.name" // Access object property -"users[0]" // Access array element -"users[0].name" // Chain property and array access -"users.*.name" // Wildcard access to all names in users -"items[*].price" // Access price of all items -``` - -Path syntax rules: - -1. Use dots (.) for object property access -2. Use brackets ([]) for array indices -3. Use asterisk (\*) as wildcard for multiple matches -4. Paths are case-sensitive -5. Properties can contain any valid JSON string characters - -## Working with Complex Data - -Example of working with nested structures: - -```motoko -let complex = obj([ - ("store", obj([ - ("inventory", arr([ - obj([ - ("id", str("item1")), - ("price", float(29.99)), - ("tags", arr([ - str("electronics"), - str("gadgets") - ])) - ]) - ])) - ])) -]); - -// Get nested value -let price = Json.get(complex, "store.inventory[0].price"); - -// Update nested array -let newTag = Json.set(complex, "store.inventory[0].tags[2]", str("new")); - -// Remove all tags -let noTags = Json.remove(complex, "store.inventory[0].tags"); -``` - -## Support & Acknowledgements - -This project was developed with the support of a developer grant from the DFINITY Foundation. This implementation is based on the ECMA-404 standard and incorporates best practices from various JSON parser implementations while being specifically optimized for the Motoko language and Internet Computer platform. - -### Community Feedback - -Your feedback is invaluable in improving this and future projects. Feel free to share your thoughts and suggestions through issues or discussions. - -### Support the Developer - -If you find this project valuable and would like to support my work on this and other open-source initiatives, you can send ICP donations to: - -```motoko -8c4ebbad19bf519e1906578f820ca4f6732ceecc1d5396e5a5713046dca251c1 -``` diff --git a/.mops/json@1.4.0/mops.toml b/.mops/json@1.4.0/mops.toml deleted file mode 100644 index 39e19b9..0000000 --- a/.mops/json@1.4.0/mops.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "json" -version = "1.4.0" -description = "JSON parser and stringifier for Motoko" -repository = "https://github.com/Demali-876/json" -keywords = [ "json", "parse", "stringfy", "validate" ] -license = "MIT" - -[dependencies] -xtended-numbers = "0.3.1" - -[dev-dependencies] -test = "2.0.0" diff --git a/.mops/json@1.4.0/src/Cursor.mo b/.mops/json@1.4.0/src/Cursor.mo deleted file mode 100644 index 5bc5d35..0000000 --- a/.mops/json@1.4.0/src/Cursor.mo +++ /dev/null @@ -1,49 +0,0 @@ -import Text "mo:base/Text"; -import Debug "mo:base/Debug"; -import Nat "mo:base/Nat"; -import Array "mo:base/Array"; - -module { - public class Cursor(t : Text) { - public let string = Text.toArray(t); - private var pos : Nat = 0; - - public func getPos() : Nat { - pos; - }; - - public func current() : Char { - if (pos < string.size()) { - string[pos]; - } else { - Debug.trap("Attempted to access character out of bounds at position " # Nat.toText(pos)); - }; - }; - - public func hasNext() : Bool { - pos < string.size(); - }; - - public func inc() { - if (pos < string.size()) { - pos += 1; - }; - }; - - public func advance(n : Nat) { - if (pos + n <= string.size()) { - pos += n; - } else { - pos := string.size(); - }; - }; - - public func substring(text : Text, start : Nat, end : Nat) : Text { - let chars = Text.toArray(text); - assert (start <= end); - assert (end <= chars.size()); - if (start == end) return ""; - Text.fromIter(Array.slice(chars, start, end)); - }; - }; -}; diff --git a/.mops/json@1.4.0/src/Lexer.mo b/.mops/json@1.4.0/src/Lexer.mo deleted file mode 100644 index d87b6c1..0000000 --- a/.mops/json@1.4.0/src/Lexer.mo +++ /dev/null @@ -1,269 +0,0 @@ -import Types "Types"; -import Cursor "Cursor"; -import Buffer "mo:base/Buffer"; -import Char "mo:base/Char"; -import Text "mo:base/Text"; -import Nat32 "mo:base/Nat32"; -import Iter "mo:base/Iter"; -import Result "mo:base/Result"; -import NatX "mo:xtended-numbers/NatX"; - -module { - public class Lexer(text : Text) { - let cursor = Cursor.Cursor(text); - let tokenBuffer = Buffer.Buffer(128); - public type Error = { - #invalidString : Text; - #invalidNumber : Text; - #invalidKeyword : Text; - #invalidChar : Text; - }; - - private func tokenizeString() : Result.Result { - cursor.inc(); - var strBuffer = Buffer.Buffer(64); - var escaped = false; - - while (cursor.hasNext()) { - let c = cursor.current(); - - if (escaped) { - escaped := false; - switch (c) { - case '\"' { strBuffer.add(Char.fromNat32(0x22)) }; - case '\\' { strBuffer.add(Char.fromNat32(0x5C)) }; - case '/' { strBuffer.add(Char.fromNat32(0x2F)) }; - case 'b' { strBuffer.add(Char.fromNat32(0x08)) }; - case 'f' { strBuffer.add(Char.fromNat32(0x0C)) }; - case 'n' { strBuffer.add(Char.fromNat32(0x0A)) }; - case 'r' { strBuffer.add(Char.fromNat32(0x0D)) }; - case 't' { strBuffer.add(Char.fromNat32(0x09)) }; - case 'u' { - cursor.inc(); - if (cursor.getPos() + 4 <= text.size()) { - let hexCode = cursor.substring(text, cursor.getPos(), (cursor.getPos() + 4)); - - var validHex = true; - var hexCount = 0; - label isHex for (c in hexCode.chars()) { - if (not isHexDigit(c)) { - validHex := false; - break isHex; - }; - hexCount += 1; - }; - if (not validHex or hexCount != 4) { - return #err(#invalidString("Invalid Unicode escape sequence: Expected exactly 4 hex digits")); - }; - - switch (NatX.fromTextAdvanced(hexCode, #hexadecimal, null)) { - case (?natValue) { - if (natValue <= 0x10FFFF) { - strBuffer.add(Char.fromNat32(Nat32.fromNat(natValue))); - cursor.advance(3); - } else { - return #err(#invalidString("Unicode value exceeds maximum allowed (0x10FFFF)")); - }; - }; - case null { - return #err(#invalidString("Invalid Unicode escape sequence: Invalid hex value")); - }; - }; - } else { - return #err(#invalidString("Incomplete Unicode escape sequence")); - }; - }; - case _ { - return #err(#invalidString("Invalid escape character: \\" # Text.fromChar(c))); - }; - }; - } else if (c == '\\') { - escaped := true; - } else if (c == '\"') { - cursor.inc(); - return #ok(#string(Text.fromIter(Iter.fromArray(Buffer.toArray(strBuffer))))); - } else { - let code = Char.toNat32(c); - if ( - (code >= 0x20 and code <= 0x21) or - (code >= 0x23 and code <= 0x5B) or - (code >= 0x5D and code <= 0x10FFFF) - ) { - strBuffer.add(c); - } else { - return #err(#invalidString("Invalid character in string: " # Text.fromChar(c))); - }; - }; - cursor.inc(); - }; - - return #err(#invalidString("Unterminated string literal")); - }; - - private func isHexDigit(c : Char) : Bool { - let code = Char.toNat32(c); - (code >= 0x30 and code <= 0x39) or (code >= 0x41 and code <= 0x46) or (code >= 0x61 and code <= 0x66); - }; - - private func tokenizeNumber() : Result.Result { - var numberBuffer = Buffer.Buffer(16); - var isFloat = false; - - if (cursor.current() == '-') { - numberBuffer.add('-'); - cursor.inc(); - }; - - if (cursor.current() == '0') { - numberBuffer.add('0'); - cursor.inc(); - } else if (Char.isDigit(cursor.current()) and cursor.current() != '0') { - while (cursor.hasNext() and Char.isDigit(cursor.current())) { - numberBuffer.add(cursor.current()); - cursor.inc(); - }; - } else { - return #err(#invalidNumber("Invalid number: Expected digit after minus sign")); - }; - - if (cursor.hasNext() and cursor.current() == '.') { - isFloat := true; - numberBuffer.add('.'); - cursor.inc(); - - if (not Char.isDigit(cursor.current())) { - return #err(#invalidNumber("Invalid number: Decimal point must be followed by digits")); - }; - - while (cursor.hasNext() and Char.isDigit(cursor.current())) { - numberBuffer.add(cursor.current()); - cursor.inc(); - }; - }; - - if (cursor.hasNext() and (cursor.current() == 'e' or cursor.current() == 'E')) { - isFloat := true; - numberBuffer.add(cursor.current()); - cursor.inc(); - - if (cursor.hasNext() and (cursor.current() == '+' or cursor.current() == '-')) { - numberBuffer.add(cursor.current()); - cursor.inc(); - }; - - if (not Char.isDigit(cursor.current())) { - return #err(#invalidNumber("Invalid number: Exponent must be followed by digits")); - }; - - while (cursor.hasNext() and Char.isDigit(cursor.current())) { - numberBuffer.add(cursor.current()); - cursor.inc(); - }; - }; - - let numberStr = Text.fromIter(Iter.fromArray(Buffer.toArray(numberBuffer))); - - if (isFloat) { - switch (Types.textToFloat(numberStr)) { - case (?num) { #ok(#number(#float(num))) }; - case null { - #err(#invalidNumber("Invalid floating point number format")); - }; - }; - } else { - switch (Types.parseInt(numberStr)) { - case (?num) { #ok(#number(#int(num))) }; - case null { - return #err(#invalidNumber("Invalid integer number format")); - }; - }; - }; - }; - private func tokenizeKeyWord() : ?Types.Token { - if (cursor.getPos() + 5 <= text.size()) { - let falsejson = cursor.substring(text, cursor.getPos(), (cursor.getPos() + 5)); - if (Text.equal(falsejson, "false")) { - cursor.advance(5); - return ?#false_; - }; - }; - if (cursor.getPos() + 4 <= text.size()) { - let nullortrue = cursor.substring(text, cursor.getPos(), (cursor.getPos() + 4)); - if (Text.equal(nullortrue, "true")) { - cursor.advance(4); - ?#true_; - } else if (Text.equal(nullortrue, "null")) { - cursor.advance(4); - ?#null_; - } else { - null; - }; - } else { - null; - }; - }; - public func tokenize() : Result.Result<[Types.Token], Error> { - label tokenizing while (cursor.hasNext()) { - let c = cursor.current(); - - switch (c) { - case (' ' or '\t' or '\n' or '\r') { - cursor.inc(); - continue tokenizing; - }; - case ('{') { - cursor.inc(); - tokenBuffer.add(#beginObject); - }; - case ('}') { - cursor.inc(); - tokenBuffer.add(#endObject); - }; - case ('[') { - cursor.inc(); - tokenBuffer.add(#beginArray); - }; - case (']') { - cursor.inc(); - tokenBuffer.add(#endArray); - }; - case (':') { - cursor.inc(); - tokenBuffer.add(#nameSeperator); - }; - case (',') { - cursor.inc(); - tokenBuffer.add(#valueSeperator); - }; - - case ('\"') { - switch (tokenizeString()) { - case (#ok(token)) { tokenBuffer.add(token) }; - case (#err(e)) { return #err(e) }; - }; - }; - - case (c) { - if (c == '-' or Char.isDigit(c)) { - switch (tokenizeNumber()) { - case (#ok(token)) { tokenBuffer.add(token) }; - case (#err(e)) { return #err(e) }; - }; - } else if (c == 'f' or c == 'n' or c == 't') { - switch (tokenizeKeyWord()) { - case (?token) { tokenBuffer.add(token) }; - case null { - return #err(#invalidKeyword("Invalid keyword starting with '" # Text.fromChar(c) # "'")); - }; - }; - } else { - return #err(#invalidChar("Unexpected character: " # Text.fromChar(c))); - }; - }; - }; - }; - - #ok(Buffer.toArray(tokenBuffer)); - }; - }; -}; diff --git a/.mops/json@1.4.0/src/Parser.mo b/.mops/json@1.4.0/src/Parser.mo deleted file mode 100644 index 41fe186..0000000 --- a/.mops/json@1.4.0/src/Parser.mo +++ /dev/null @@ -1,572 +0,0 @@ -import Types "./Types"; -import Result "mo:base/Result"; -import Array "mo:base/Array"; -import Buffer "mo:base/Buffer"; -import Text "mo:base/Text"; -import Nat "mo:base/Nat"; - -module { - type Json = Types.Json; - public class Parser(tokens : [Types.Token]) { - var position = 0; - - private func current() : ?Types.Token { - if (position < tokens.size()) ?tokens[position] else null; - }; - - private func advance() { - position += 1; - }; - - public func parse() : Result.Result { - switch (parseValue()) { - case (#ok(json)) { - switch (current()) { - case (null) { #ok(json) }; - case (?_) { #err(#unexpectedToken("Expected end of input")) }; - }; - }; - case (#err(e)) { #err(e) }; - }; - }; - - private func parseValue() : Result.Result { - switch (current()) { - case (null) { #err(#unexpectedEOF) }; - case (?token) { - switch (token) { - case (#beginObject) { parseObject() }; - case (#beginArray) { parseArray() }; - case (#string(s)) { advance(); #ok(#string(s)) }; - case (#number(n)) { advance(); #ok(#number(n)) }; - case (#true_) { advance(); #ok(#bool(true)) }; - case (#false_) { advance(); #ok(#bool(false)) }; - case (#null_) { advance(); #ok(#null_) }; - case (_) { #err(#unexpectedToken("Expected value")) }; - }; - }; - }; - }; - - private func parseObject() : Result.Result { - advance(); - var fields : [(Text, Types.Json)] = []; - - switch (current()) { - case (?#endObject) { - advance(); - #ok(#object_(fields)); - }; - case (?#string(_)) { - switch (parseMember()) { - case (#err(e)) { #err(e) }; - case (#ok(field)) { - fields := [(field.0, field.1)]; - loop { - switch (current()) { - case (?#valueSeperator) { - advance(); - switch (parseMember()) { - case (#ok(next)) { - fields := Array.append(fields, [(next.0, next.1)]); - }; - case (#err(e)) { return #err(e) }; - }; - }; - case (?#endObject) { - advance(); - return #ok(#object_(fields)); - }; - case (null) { return #err(#unexpectedEOF) }; - case (_) { - return #err(#unexpectedToken("Expected ',' or '}'")); - }; - }; - }; - }; - }; - }; - case (null) { #err(#unexpectedEOF) }; - case (_) { #err(#unexpectedToken("Expected string or '}'")) }; - }; - }; - - private func parseMember() : Result.Result<(Text, Types.Json), Types.Error> { - switch (current()) { - case (?#string(key)) { - advance(); - switch (current()) { - case (?#nameSeperator) { - advance(); - switch (parseValue()) { - case (#ok(value)) { #ok((key, value)) }; - case (#err(e)) { #err(e) }; - }; - }; - case (null) { #err(#unexpectedEOF) }; - case (_) { #err(#unexpectedToken("Expected ':'")) }; - }; - }; - case (null) { #err(#unexpectedEOF) }; - case (_) { #err(#unexpectedToken("Expected string")) }; - }; - }; - - private func parseArray() : Result.Result { - advance(); - var elements : [Types.Json] = []; - - switch (current()) { - case (?#endArray) { - advance(); - #ok(#array(elements)); - }; - case (null) { - #err(#unexpectedEOF); - }; - case (_) { - switch (parseValue()) { - case (#err(e)) { #err(e) }; - case (#ok(value)) { - elements := [value]; - loop { - switch (current()) { - case (?#valueSeperator) { - advance(); - switch (parseValue()) { - case (#ok(next)) { - elements := Array.append(elements, [next]); - }; - case (#err(e)) { return #err(e) }; - }; - }; - case (?#endArray) { - advance(); - return #ok(#array(elements)); - }; - case (null) { return #err(#unexpectedEOF) }; - case (_) { - return #err(#unexpectedToken("Expected ',' or ']'")); - }; - }; - }; - }; - }; - }; - }; - }; - }; - - public func parsePath(path : Text) : [Types.PathPart] { - let chars = path.chars(); - let parts = Buffer.Buffer(8); - var current = Buffer.Buffer(16); - var inBracket = false; - - for (c in chars) { - switch (c) { - case '[' { - if (current.size() > 0) { - parts.add(#key(Text.fromIter(current.vals()))); - current.clear(); - }; - inBracket := true; - }; - case ']' { - if (current.size() > 0) { - let indexText = Text.fromIter(current.vals()); - if (indexText == "*") { - parts.add(#wildcard); - } else { - switch (Nat.fromText(indexText)) { - case (?idx) { parts.add(#index(idx)) }; - case null {}; - }; - }; - current.clear(); - }; - inBracket := false; - }; - case '.' { - if (current.size() > 0) { - let key = Text.fromIter(current.vals()); - if (key == "*") { - parts.add(#wildcard); - } else { - parts.add(#key(key)); - }; - current.clear(); - }; - }; - case c { current.add(c) }; - }; - }; - if (current.size() > 0) { - let final = Text.fromIter(current.vals()); - if (final == "*") { - parts.add(#wildcard); - } else { - parts.add(#key(final)); - }; - }; - - Buffer.toArray(parts); - }; - - public func getWithParts(json : Json, parts : [Types.PathPart]) : ?Json { - if (parts.size() == 0) { return ?json }; - - switch (parts[0], json) { - case (#key(key), #object_(entries)) { - for ((k, v) in entries.vals()) { - if (k == key) { - return getWithParts( - v, - Array.tabulate( - parts.size() - 1, - func(i) = parts[i + 1], - ), - ); - }; - }; - null; - }; - case (#index(i), #array(items)) { - if (i < items.size()) { - getWithParts( - items[i], - Array.tabulate( - parts.size() - 1, - func(i) = parts[i + 1], - ), - ); - } else { - null; - }; - }; - case (#wildcard, #object_(entries)) { - ?#array( - Array.mapFilter<(Text, Json), Json>( - entries, - func((_, v)) = getWithParts( - v, - Array.tabulate( - parts.size() - 1, - func(i) = parts[i + 1], - ), - ), - ) - ); - }; - case (#wildcard, #array(items)) { - ?#array( - Array.mapFilter( - items, - func(item) = getWithParts( - item, - Array.tabulate( - parts.size() - 1, - func(i) = parts[i + 1], - ), - ), - ) - ); - }; - case _ { null }; - }; - }; - - public func setWithParts(json : Json, parts : [Types.PathPart], newValue : Json) : Json { - if (parts.size() == 0) { - return newValue; - }; - - switch (parts[0], json) { - case (#key(key), #object_(entries)) { - let remaining = Array.tabulate( - parts.size() - 1, - func(i) = parts[i + 1], - ); - - var found = false; - let newEntries = Array.map<(Text, Json), (Text, Json)>( - entries, - func((k, v) : (Text, Json)) : (Text, Json) { - if (k == key) { - found := true; - (k, setWithParts(v, remaining, newValue)); - } else { (k, v) }; - }, - ); - - if (not found) { - #object_(Array.append(newEntries, [(key, setWithParts(#null_, remaining, newValue))])); - } else { - #object_(newEntries); - }; - }; - - case (#index(i), #array(items)) { - let remaining = Array.tabulate( - parts.size() - 1, - func(i) = parts[i + 1], - ); - - if (i < items.size()) { - #array( - Array.tabulate( - items.size(), - func(idx : Nat) : Json { - if (idx == i) { - setWithParts(items[idx], remaining, newValue); - } else { - items[idx]; - }; - }, - ) - ); - } else { - let nulls = Array.tabulate( - i - items.size(), - func(_) = #null_, - ); - #array( - Array.append( - Array.append(items, nulls), - [setWithParts(#null_, remaining, newValue)], - ) - ); - }; - }; - - case (#key(key), _) { - let remaining = Array.tabulate( - parts.size() - 1, - func(i) = parts[i + 1], - ); - #object_([(key, setWithParts(#null_, remaining, newValue))]); - }; - - case (#index(i), _) { - let remaining = Array.tabulate( - parts.size() - 1, - func(i) = parts[i + 1], - ); - let items = Array.tabulate( - i + 1, - func(idx : Nat) : Json { - if (idx == i) { - setWithParts(#null_, remaining, newValue); - } else { - #null_; - }; - }, - ); - #array(items); - }; - - case _ { json }; - }; - }; - - public func removeWithParts(json : Json, parts : [Types.PathPart]) : Json { - if (parts.size() == 0) { - return #null_; - }; - - switch (parts[0], json) { - case (#key(key), #object_(entries)) { - if (parts.size() == 1) { - #object_( - Array.filter<(Text, Json)>( - entries, - func((k, _) : (Text, Json)) : Bool { k != key }, - ) - ); - } else { - let remaining = Array.tabulate( - parts.size() - 1, - func(i) = parts[i + 1], - ); - - #object_( - Array.map<(Text, Json), (Text, Json)>( - entries, - func((k, v) : (Text, Json)) : (Text, Json) { - if (k == key) { (k, removeWithParts(v, remaining)) } else { - (k, v); - }; - }, - ) - ); - }; - }; - - case (#index(i), #array(items)) { - if (i >= items.size()) { - return json; - }; - - if (parts.size() == 1) { - #array( - Array.tabulate( - items.size() - 1, - func(idx : Nat) : Json { - if (idx < i) { - items[idx]; - } else { - items[idx + 1]; - }; - }, - ) - ); - } else { - let remaining = Array.tabulate( - parts.size() - 1, - func(i) = parts[i + 1], - ); - - #array( - Array.tabulate( - items.size(), - func(idx : Nat) : Json { - if (idx == i) { - removeWithParts(items[idx], remaining); - } else { - items[idx]; - }; - }, - ) - ); - }; - }; - case _ { json }; - }; - }; - - public func validate(instance : Json, schema : Types.Schema) : Result.Result<(), Types.ValidationError> { - switch (schema) { - case (#object_ { properties; required }) { - switch (instance) { - case (#object_(entries)) { - switch (required) { - case (?requiredFields) { - for (requiredKey in requiredFields.vals()) { - var found = false; - label checking for ((key, _) in entries.vals()) { - if (key == requiredKey) { - found := true; - break checking; - }; - }; - if (not found) { - return #err(#requiredField(requiredKey)); - }; - }; - }; - case null {}; - }; - for ((schemaKey, schemaType) in properties.vals()) { - for ((key, value) in entries.vals()) { - if (key == schemaKey) { - switch (validate(value, schemaType)) { - case (#err(e)) return #err(e); - case (#ok()) {}; - }; - }; - }; - }; - #ok(); - }; - case (_) { - #err( - #typeError { - expected = "object"; - got = Types.getTypeString(instance); - path = ""; - } - ); - }; - }; - }; - case (#array { items }) { - switch (instance) { - case (#array(values)) { - for (value in values.vals()) { - switch (validate(value, items)) { - case (#err(e)) return #err(e); - case (#ok()) {}; - }; - }; - #ok(); - }; - case (_) { - #err( - #typeError { - expected = "array"; - got = Types.getTypeString(instance); - path = ""; - } - ); - }; - }; - }; - case (#string) { - switch (instance) { - case (#string(_)) #ok(); - case (_) { - #err( - #typeError { - expected = "string"; - got = Types.getTypeString(instance); - path = ""; - } - ); - }; - }; - }; - case (#number) { - switch (instance) { - case (#number(_)) #ok(); - case (_) { - #err( - #typeError { - expected = "number"; - got = Types.getTypeString(instance); - path = ""; - } - ); - }; - }; - }; - case (#boolean) { - switch (instance) { - case (#bool(_)) #ok(); - case (_) { - #err( - #typeError { - expected = "boolean"; - got = Types.getTypeString(instance); - path = ""; - } - ); - }; - }; - }; - case (#null_) { - switch (instance) { - case (#null_) #ok(); - case (_) { - #err( - #typeError { - expected = "null"; - got = Types.getTypeString(instance); - path = ""; - } - ); - }; - }; - }; - }; - }; -}; diff --git a/.mops/json@1.4.0/src/Types.mo b/.mops/json@1.4.0/src/Types.mo deleted file mode 100644 index 78261f7..0000000 --- a/.mops/json@1.4.0/src/Types.mo +++ /dev/null @@ -1,445 +0,0 @@ -import Text "mo:base/Text"; -import Char "mo:base/Char"; -import Int "mo:base/Int"; -import Int32 "mo:base/Int32"; -import Float "mo:base/Float"; -import Bool "mo:base/Bool"; -import Iter "mo:base/Iter"; -import Array "mo:base/Array"; -import Buffer "mo:base/Buffer"; -import Nat32 "mo:base/Nat32"; - -module { - public type Path = Text; - public type PathPart = { - #key : Text; - #index : Nat; - #wildcard; - }; - public type Schema = { - #object_ : { - properties : [(Text, Schema)]; - required : ?[Text]; - }; - #array : { - items : Schema; - }; - #string; - #number; - #boolean; - #null_; - }; - - public type ValidationError = { - #typeError : { - expected : Text; - got : Text; - path : Text; - }; - #requiredField : Text; - }; - public type Token = { - #beginArray; - #beginObject; - #endArray; - #endObject; - #nameSeperator; - #valueSeperator; - #whitespace; - #false_; - #null_; - #true_; - #number : { - #int : Int; - #float : Float; - }; - #string : Text; - }; - public type Json = { - #object_ : [(Text, Json)]; - #array : [Json]; - #string : Text; - #number : { - #int : Int; - #float : Float; - }; - #bool : Bool; - #null_; - }; - - public type Error = { - #invalidString : Text; - #invalidNumber : Text; - #invalidKeyword : Text; - #invalidChar : Text; - #invalidValue : Text; - #unexpectedEOF; - #unexpectedToken : Text; - }; - - public func transform(json : Json, replacer : (Text, Json) -> ?Json, key : Text) : Json { - let replaced = switch (replacer(key, json)) { - case (?newValue) { newValue }; - case (null) { json }; - }; - - switch (replaced) { - case (#object_(entries)) { - #object_( - Array.map<(Text, Json), (Text, Json)>( - entries, - func((k, v) : (Text, Json)) : (Text, Json) = (k, transform(v, replacer, k)), - ) - ); - }; - case (#array(items)) { - #array( - Array.map( - items, - func(item : Json) : Json = transform(item, replacer, key), - ) - ); - }; - case _ { replaced }; - }; - }; - public func filterByKeys(json : Json, keys : [Text]) : Json { - switch (json) { - case (#object_(entries)) { - #object_( - Array.filter<(Text, Json)>( - entries, - func((k, _) : (Text, Json)) : Bool { - for (allowedKey in keys.vals()) { - if (k == allowedKey) return true; - }; - false; - }, - ) - ); - }; - case (#array(items)) { - #array( - Array.map( - items, - func(item : Json) : Json = filterByKeys(item, keys), - ) - ); - }; - case _ { json }; - }; - }; - - public func charAt(i : Nat, t : Text) : Char { - let arr = Text.toArray(t); - arr[i]; - }; - func to4DigitHex(n: Nat32) : Text { - let hex_chars = "0123456789abcdef"; - var s = ""; - var i = n; - var counter : Nat = 0; - - while (counter < 4) { - // Get the last 4 bits to find the hex character index. - let index = Nat32.toNat(i & 0xF); - // Prepend the character to build the string in the correct order. - s := Text.fromChar(Text.toArray(hex_chars)[index]) # s; - // Shift bits for the next character. - i >>= 4; - // Increment the counter. - counter += 1; - }; - return s; - }; - // A helper function to correctly escape a string for JSON. - public func escape(s: Text) : Text { - let buf = Buffer.Buffer(s.size()); // Pre-allocate buffer for performance. - for (c in s.chars()) { - switch (c) { - case ('\"') { buf.add("\\\"") }; - case ('\\') { buf.add("\\\\") }; - case ('\n') { buf.add("\\n") }; - case ('\r') { buf.add("\\r") }; - case ('\t') { buf.add("\\t") }; - // Note: Motoko Char doesn't have literals for \b and \f, - // so we handle them in the default case via their code points. - case _ { - let code = Char.toNat32(c); - if (code == 0x8) { // Backspace - buf.add("\\b"); - } else if (code == 0xC) { // Form feed - buf.add("\\f"); - } else if (code < 32) { // Other control characters (U+0000 to U+001F) - buf.add("\\u" # to4DigitHex(code)); - } else { // A regular, non-special character. - buf.add(Text.fromChar(c)); - }; - }; - }; - }; - return Buffer.foldLeft(buf, "", func(acc, part) { acc # part }); - }; - public func toText(json : Json) : Text { - switch (json) { - case (#object_(entries)) { - let fields = entries.vals(); - var result = "{"; - var first = true; - for ((key, value) in fields) { - if (not first) { result #= "," }; - result #= "\"" # key # "\":" # toText(value); - first := false; - }; - result # "}"; - }; - case (#array(items)) { - let values = items.vals(); - var result = "["; - var first = true; - for (item in values) { - if (not first) { result #= "," }; - result #= toText(item); - first := false; - }; - result # "]"; - }; - case (#string(text)) { "\"" # escape(text) # "\"" }; - case (#number(#int(n))) { Int.toText(n) }; - case (#number(#float(n))) { Float.format(#exact, n) }; - case (#bool(b)) { Bool.toText(b) }; - case (#null_) { "null" }; - }; - }; - func charToInt(c : Char) : Int { - Int32.toInt(Int32.fromNat32(Char.toNat32(c) - 48)); - }; - - public func textToFloat(text : Text) : ?Float { - var integer : Int = 0; - var fraction : Float = 0; - var isNegative = false; - var position : Nat = 0; - let chars = text.chars(); - - if (Text.size(text) == 0) { - return null - }; - let firstchar = Text.toArray(text)[0]; - - if(firstchar == '-' and text.size()== 1){ - return null; - }; - if (firstchar == 'e' or firstchar == 'E'){ - return null - }; - - switch (chars.next()) { - case (?'-') { - isNegative := true; - position += 1 - }; - case (?'+') { - position += 1 - }; - case (?'.') { - position += 1; - switch (chars.next()) { - case (?d) if (Char.isDigit(d)) { - fraction := 0.1 * Float.fromInt(charToInt(d)); - position += 1 - }; - case (_) { return null } - } - }; - case (?d) if (Char.isDigit(d)) { - integer := charToInt(d); - position += 1 - }; - case (_) { return null } - }; - - var hasDigits = position > 0; - label integer loop { - switch (chars.next()) { - case (?d) { - if (Char.isDigit(d)) { - integer := integer * 10 + charToInt(d); - position += 1; - hasDigits := true - } else if (d == '.') { - position += 1; - break integer - } else if (d == 'e' or d == 'E') { - position += 1; - if (not hasDigits) { - return null - }; - - var expResult = parseExponent(chars); - switch (expResult) { - case (null) { - return null; - }; - case (?(expValue, _)) { - // Calculate final value with exponent - let base = Float.fromInt(if (isNegative) -integer else integer) + - (if (isNegative) -fraction else fraction); - let multiplier = Float.pow(10, Float.fromInt(expValue)); - return ?(base * multiplier) - } - } - } else { - return null - } - }; - case (null) { - if (not hasDigits) { - return null; - }; - return ?(Float.fromInt(if (isNegative) -integer else integer)) - } - } - }; - - var fractionMultiplier : Float = 0.1; - var hasFractionDigits = false; - - label fraction loop { - switch (chars.next()) { - case (?d) { - if (Char.isDigit(d)) { - fraction += fractionMultiplier * Float.fromInt(charToInt(d)); - fractionMultiplier *= 0.1; - position += 1; - hasFractionDigits := true - } else if (d == 'e' or d == 'E') { - position += 1; - - if (not (hasDigits or hasFractionDigits)) { - return null - }; - - // Handle exponent part - var expResult = parseExponent(chars); - switch (expResult) { - case (null) { - return null; // Invalid exponent format - }; - case (?(expValue, _)) { - // Calculate final value with exponent - let base = Float.fromInt(if (isNegative) -integer else integer) + - (if (isNegative) -fraction else fraction); - let multiplier = Float.pow(10, Float.fromInt(expValue)); - return ?(base * multiplier) - } - } - } else { - return null - } - }; - case (null) { - // End of input - return complete number - let result = Float.fromInt(if (isNegative) -integer else integer) + - (if (isNegative) -fraction else fraction); - return ?result - } - } - }; - - return null; - }; - - func parseExponent(chars : Iter.Iter) : ?(Int, Nat) { - var exponent : Int = 0; - var expIsNegative = false; - var position = 0; - var hasDigits = false; - - // Parse optional sign or first digit - switch (chars.next()) { - case (?d) { - if (d == '-') { - expIsNegative := true; - position += 1 - } else if (d == '+') { - position += 1 - } else if (Char.isDigit(d)) { - exponent := charToInt(d); - position += 1; - hasDigits := true - } else { - return null - } - }; - case (null) {return null}; - }; - - label exponent loop { - switch (chars.next()) { - case (?d) { - if (Char.isDigit(d)) { - exponent := exponent * 10 + charToInt(d); - position += 1; - hasDigits := true - } else { - return null; - } - }; - case (null) { - if (not hasDigits) { - return null; - }; - return ?(if (expIsNegative) -exponent else exponent, position) - } - } - }; - - return null; - }; - - public func texttofloat(t:Text): async ?Float{ - textToFloat(t); - }; - public func parseInt(text : Text) : ?Int { - var int : Int = 0; - var isNegative = false; - let chars = text.chars(); - - switch (chars.next()) { - case (?'-') { - isNegative := true; - }; - case (?d) if (Char.isDigit(d)) { - int := Int32.toInt(Int32.fromNat32(Char.toNat32(d) - 48)); - }; - case (_) { return null }; - }; - - label parsing loop { - switch (chars.next()) { - case (?d) { - if (Char.isDigit(d)) { - int := int * 10 + Int32.toInt(Int32.fromNat32(Char.toNat32(d) - 48)); - } else { - return null; - }; - }; - case (null) { - return ?(if (isNegative) -int else int); - }; - }; - }; - return null; - }; - public func getTypeString(json : Json) : Text { - switch (json) { - case (#object_(_)) "object"; - case (#array(_)) "array"; - case (#string(_)) "string"; - case (#number(_)) "number"; - case (#bool(_)) "boolean"; - case (#null_) "null"; - }; - }; -}; diff --git a/.mops/json@1.4.0/src/lib.mo b/.mops/json@1.4.0/src/lib.mo deleted file mode 100644 index 83742a1..0000000 --- a/.mops/json@1.4.0/src/lib.mo +++ /dev/null @@ -1,146 +0,0 @@ -import Lexer "Lexer"; -import Parser "Parser"; -import Types "Types"; -import Result "mo:base/Result"; -import Text "mo:base/Text"; -import Int "mo:base/Int"; -import Float "mo:base/Float"; -module Json { - public type Json = Types.Json; - public type Replacer = { - #function : (Text, Json) -> ?Json; - #keys : [Text]; - }; - public type GetAsError = { - #pathNotFound; - #typeMismatch; - }; - public type Path = Types.Path; - public type Error = Types.Error; - public func errToText(e : Error) : Text { - switch (e) { - case (#invalidString(err)) { err }; - case (#invalidNumber(err)) { err }; - case (#invalidKeyword(err)) { err }; - case (#invalidChar(err)) { err }; - case (#invalidValue(err)) { err }; - case (#unexpectedEOF()) { "Unexpected EOF" }; - case (#unexpectedToken(err)) { err }; - }; - }; - public type Schema = Types.Schema; - public type ValidationError = Types.ValidationError; - //Json Type constructors - public func str(text : Text) : Json = #string(text); - public func int(n : Int) : Json = #number(#int(n)); - public func float(n : Float) : Json = #number(#float(n)); - public func bool(b : Bool) : Json = #bool(b); - public func nullable() : Json = #null_; - public func obj(entries : [(Text, Json)]) : Json = #object_(entries); - public func arr(items : [Json]) : Json = #array(items); - //Schema Type constructors - public func string() : Types.Schema = #string; - public func number() : Types.Schema = #number; - public func boolean() : Types.Schema = #boolean; - public func nullSchema() : Types.Schema = #null_; - public func array(itemSchema : Types.Schema) : Types.Schema = #array({ - items = itemSchema; - }); - public func schemaObject( - properties : [(Text, Types.Schema)], - required : ?[Text], - ) : Types.Schema = #object_({ - properties; - required; - }); - - public func parse(input : Text) : Result.Result { - let lexer = Lexer.Lexer(input); - let tokens = switch (lexer.tokenize()) { - case (#ok(tokens)) { tokens }; - case (#err(e)) { return #err(e) }; - }; - let parser = Parser.Parser(tokens); - parser.parse(); - }; - - public func stringify(json : Json, replacer : ?Replacer) : Text { - switch (replacer) { - case (null) { - Types.toText(json); - }; - case (?#function(fn)) { - Types.toText(Types.transform(json, fn, "")); - }; - case (?#keys(allowedKeys)) { - Types.toText(Types.filterByKeys(json, allowedKeys)); - }; - }; - }; - public func get(json : Json, path : Types.Path) : ?Json { - let parts = Parser.parsePath(path); - Parser.getWithParts(json, parts); - }; - - public func getAsNat(json : Json, path : Types.Path) : Result.Result { - let ?value = get(json, path) else return #err(#pathNotFound); - let #number(#int(intValue)) = value else return #err(#typeMismatch); - if (intValue < 0) { - // Must be a positive integer - return #err(#typeMismatch); - }; - #ok(Int.abs(intValue)); - }; - - public func getAsInt(json : Json, path : Types.Path) : Result.Result { - let ?value = get(json, path) else return #err(#pathNotFound); - let #number(#int(intValue)) = value else return #err(#typeMismatch); - #ok(intValue); - }; - - public func getAsFloat(json : Json, path : Types.Path) : Result.Result { - let ?value = get(json, path) else return #err(#pathNotFound); - let #number(numberValue) = value else return #err(#typeMismatch); - let floatValue = switch (numberValue) { - case (#int(intValue)) { Float.fromInt(intValue) }; - case (#float(floatValue)) { floatValue }; - }; - #ok(floatValue); - }; - - public func getAsBool(json : Json, path : Types.Path) : Result.Result { - let ?value = get(json, path) else return #err(#pathNotFound); - let #bool(boolValue) = value else return #err(#typeMismatch); - #ok(boolValue); - }; - - public func getAsText(json : Json, path : Types.Path) : Result.Result { - let ?value = get(json, path) else return #err(#pathNotFound); - let #string(text) = value else return #err(#typeMismatch); - #ok(text); - }; - - public func getAsArray(json : Json, path : Types.Path) : Result.Result<[Json], GetAsError> { - let ?value = get(json, path) else return #err(#pathNotFound); - let #array(items) = value else return #err(#typeMismatch); - #ok(items); - }; - - public func getAsObject(json : Json, path : Types.Path) : Result.Result<[(Text, Json)], GetAsError> { - let ?value = get(json, path) else return #err(#pathNotFound); - let #object_(entries) = value else return #err(#typeMismatch); - #ok(entries); - }; - - public func set(json : Json, path : Types.Path, newValue : Json) : Json { - let parts = Parser.parsePath(path); - Parser.setWithParts(json, parts, newValue); - }; - public func remove(json : Json, path : Types.Path) : Json { - let parts = Parser.parsePath(path); - Parser.removeWithParts(json, parts); - }; - public func validate(json : Json, schema : Types.Schema) : Result.Result<(), Types.ValidationError> { - Parser.validate(json, schema); - }; -}; diff --git a/.mops/sha2@0.2.5/LICENSE b/.mops/sha2@0.2.5/LICENSE deleted file mode 100644 index 8f2c7b8..0000000 --- a/.mops/sha2@0.2.5/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2023 - 2026 MR Research AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/.mops/sha2@0.2.5/NOTICE b/.mops/sha2@0.2.5/NOTICE deleted file mode 100644 index 119bdba..0000000 --- a/.mops/sha2@0.2.5/NOTICE +++ /dev/null @@ -1,12 +0,0 @@ -Copyright 2023 - 2026 MR Research AG - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. diff --git a/.mops/sha2@0.2.5/README.md b/.mops/sha2@0.2.5/README.md deleted file mode 100644 index 9406dec..0000000 --- a/.mops/sha2@0.2.5/README.md +++ /dev/null @@ -1,338 +0,0 @@ -[![mops](https://oknww-riaaa-aaaam-qaf6a-cai.raw.ic0.app/badge/mops/sha2)](https://mops.one/sha2) -[![documentation](https://oknww-riaaa-aaaam-qaf6a-cai.raw.ic0.app/badge/documentation/sha2)](https://mops.one/sha2/docs) - -# SHA2 family - -Optimized implementation of all SHA2 functions - -## Overview - -This package implements all SHA2 functions: - -- sha256 -- sha224 -- sha512 -- sha384 -- sha512-256 -- sha512-224 - -The API allows to hash types `Blob`, `[Nat8]`, `[var Nat8]`, `Iter`, and `List`. - -The API provides a Digest type which accepts the message piecewise until finally computing the hash sum (digest). -This allows hashing very large messages over multiple executions of the canister, even across canister upgrades. - -### Links - -The package is published on [MOPS](https://mops.one/sha2) and [GitHub](https://github.com/research-ag/sha2). -Please refer to the README on GitHub where it renders properly with formulas and tables. - -The API documentation can be found [here](https://mops.one/sha2/docs/lib) on Mops. - -For updates, help, questions, feedback and other requests related to this package join us on: - -- [OpenChat group](https://oc.app/2zyqk-iqaaa-aaaar-anmra-cai) -- [Twitter](https://twitter.com/mr_research_ag) -- [Dfinity forum](https://forum.dfinity.org/) - -## Usage - -### Install with mops - -You need `mops` installed. In your project directory run: - -```bash -mops init -mops add sha2 -``` - -In the Motoko source file import the package as: - -```motoko -import Sha256 "mo:sha2/Sha256"; -import Sha512 "mo:sha2/Sha512"; - -``` - -In your `dfx.json` make sure you have the entry: - -``` -"defaults": { - "build": { - "args": "", - "packtool": "mops sources" - } - }, -``` - -## Examples - -### 1. Quick hashing with convenience functions - -The simplest way to hash a complete message is using the shortcut functions: - -```motoko -import Sha256 "mo:sha2/Sha256"; -import Sha512 "mo:sha2/Sha512"; - -// Hash from Blob -let hash1 : Blob = Sha256.fromBlob(#sha256, "Hello, World!"); -let hash2 : Blob = Sha512.fromBlob(#sha512, "Hello, World!"); - -// Hash from Array -let data : [Nat8] = [72, 101, 108, 108, 111]; -let hash3 : Blob = Sha256.fromArray(#sha224, data); - -// Hash from VarArray -let varData : [var Nat8] = [var 72, 101, 108, 108, 111]; -let hash4 : Blob = Sha512.fromVarArray(#sha384, varData); - -// Hash from positional byte accessor function -func getByte(i : Nat) : Nat8 { 0; /* return byte at position i */ }; -let accessorLen = 100; // number of bytes to read -let hash5 : Blob = Sha256.fromAccessor(#sha256, getByte, 0, accessorLen); - -// Hash from next-byte reader function -var pos = 0; -func nextByte() : Nat8 { pos += 1; 0; /* return next byte */ }; -let readerLen = 100; // number of bytes to read -let hash6 : Blob = Sha512.fromReader(#sha512_256, nextByte, readerLen); - -// Hash from Iter -let iter = [72, 101, 108, 108, 111].vals(); -let hash7 : Blob = Sha256.fromIter(#sha256, iter); - -``` - -To hash from `List` the most efficient way is to use the reader function as follows: - -```motoko -// Hash from List -import List "mo:core/List"; - -let list = List.fromArray([72, 101, 108, 108, 111]); -let hash8 : Blob = Sha512.fromReader(#sha512, list.reader(0), List.size(list)); - -``` - -### 2. Streaming API with Digest engine - -For processing data in chunks, create a `Digest` type and write to it incrementally: - -```motoko -import Sha256 "mo:sha2/Sha256"; - -// Create a new digest engine -let digest = Sha256.new(); - -// Write data in chunks of different types -digest.writeBlob("First chunk "); -digest.writeArray([115, 101, 99, 111, 110, 100]); // "second" -digest.writeBlob(" chunk"); - -let varData : [var Nat8] = [var 32, 116, 104, 105, 114, 100]; // " third" -digest.writeVarArray(varData); - -// Write from positional function -func getChunk(i : Nat) : Nat8 { 0; /* return byte at position i */ }; -digest.writeAccessor(getChunk, 0, 10); - -// Write from reader function -var index = 0; -func nextChunk() : Nat8 { index += 1; 0; /* return next byte */ }; -digest.writeReader(nextChunk, 5); - -// Finalize and get the hash -let finalHash : Blob = digest.sum(); - -// Note: After calling sum(), the digest is consumed and cannot be reused -// Attempting to write or sum again will trap - -``` - -The first argument `#sha256` in the `Sha256` module functions and `#sha512` in the `Sha512` is implicit and can be skipped when writing code. For example, `Sha512.new(#sha512)` can be written as `Sha512.new()`. - -### 3. Cloning for intermediate hashes - -To get an intermediate hash without consuming the digest, `clone()` it and -finalize the clone — your original keeps accumulating: - -```motoko -import Sha256 "mo:sha2/Sha256"; -import Debug "mo:core/Debug"; - -let digest = Sha256.new(); - -// Hash first chunk -digest.writeBlob("Chunk 1"); -let hash1 = digest.clone().sum(); // intermediate hash; `digest` stays open -Debug.print("Hash after chunk 1: " # debug_show (hash1)); - -// Hash second chunk -digest.writeBlob("Chunk 2"); -let hash2 = digest.clone().sum(); -Debug.print("Hash after chunk 2: " # debug_show (hash2)); - -// Hash third chunk -digest.writeBlob("Chunk 3"); -let hash3 = digest.clone().sum(); -Debug.print("Hash after chunk 3: " # debug_show (hash3)); - -// Final hash (consumes `digest`) -let finalHash = digest.sum(); -Debug.print("Final hash: " # debug_show (finalHash)); - -``` - -Each `clone().sum()` allocates a copy of the digest plus the result `Blob`, so -reach for it only when you genuinely need a mid-stream snapshot. If you instead -want to read the hash of an already-finalized digest more than once, use -`readSum()` — it re-reads the closed state without re-finalizing. - -### 4. Stable state across upgrades - -For hashing very large messages across multiple message executions and even upgrades: - -```motoko -import Sha256 "mo:sha2/Sha256"; - -actor { - // Declare digest as stable - stable var digestState : ?Sha256.DigestShared = null; - - // Initialize on first call - public func initDigest() : async () { - let d = Sha256.new(); - digestState := ?d.share(); - }; - - // Write a chunk (can be called multiple times across different messages) - public func writeChunk(data : Blob) : async () { - switch (digestState) { - case null { assert false }; // Must call initDigest first - case (?state) { - let d = Sha256.unshare(state); - d.writeBlob(data); - digestState := ?d.share(); // Save updated state - }; - }; - }; - - // Get intermediate hash without finalizing - public query func peekHash() : async ?Blob { - switch (digestState) { - case null { null }; - case (?state) { - let d = Sha256.unshare(state); - ?d.clone().sum(); - }; - }; - }; - - // Finalize and get the hash - public func finalizeHash() : async ?Blob { - switch (digestState) { - case null { null }; - case (?state) { - let d = Sha256.unshare(state); - let hash = d.sum(); - digestState := null; // Clear the consumed digest - ?hash; - }; - }; - }; - - // Reset to start a new hash - public func resetDigest() : async () { - switch (digestState) { - case null {}; - case (?state) { - let d = Sha256.unshare(state); - d.reset(); - digestState := ?d.share(); - }; - }; - }; - - // Example: Hash a large file in chunks across multiple calls - public func hashLargeFile(chunks : [Blob]) : async Blob { - let d = Sha256.new(); - for (chunk in chunks.vals()) { - d.writeBlob(chunk); - }; - d.sum(); - }; -}; - -``` - -### Build & test - -Run: - -```bash -git clone git@github.com:research-ag/sha2.git -mops install -mops test -``` - -## Benchmarks - -### Mops benchmark - -Run - -```bash -mops bench -``` - -or - -```bash -mops bench --replica pocket-ic -``` - -or look at the [benchmark on mops](https://mops.one/sha2/benchmarks). - -### Performance - -We measure performance with random input messages created by the [Prng package](https://mops.one/prng). Measuring with a message of all the same bytes is not a reliable way to measure. It produces significantly different results. - -### Memory - -The hash engines are designed to not make any heap allocations when consuming the message. -This can be seen in the benchmark results. - -By this statement we mean that the heap allocations do not depend linearly on the message length. -There is a constant heap allocation when the hash engine (Digest instance) is created. -There may also be a constant heap allocation every time a writer function (e.g. `writeBlob`, etc.) is called. -But the heap allocation does not increase with the message length. - -This is true for the Sha256 and Sha512 engines. -It is also true for all different writer functions `writeBlob, writeArray, writeVarArray, writeReader, writeAccessor, writeIter`. - -## Implementation notes - -The round loops are unrolled. -This was mainly motivated by reducing the heap allocations but it also reduced the instructions significantly. - -## Contributing - -### Formatting - -To format the code, run: - -```bash -npx -y prettier --plugin prettier-plugin-motoko --write '**/*.{mo,json,md}' -``` - -## Copyright - -MR Research AG, 2023-2026 - -## Authors - -Main author: Timo Hanke (timohanke) - -## License - -Apache-2.0 diff --git a/.mops/sha2@0.2.5/examples/Merkle.mo b/.mops/sha2@0.2.5/examples/Merkle.mo deleted file mode 100644 index 0901f49..0000000 --- a/.mops/sha2@0.2.5/examples/Merkle.mo +++ /dev/null @@ -1,146 +0,0 @@ -/// Example: an allocation-free Merkle tree with `mo:sha2`. -/// -/// A Merkle tree hashes pairs of nodes up to a single root. Done the obvious -/// way, every internal node produces an intermediate digest `Blob`, so a tree -/// over N leaves allocates ~N `Blob`s. On the IC that garbage adds up fast. -/// -/// This example builds the same tree with ZERO per-node allocation — the only -/// `Blob` allocated is the root you get back. It uses two combine primitives, -/// each of which is a SINGLE SHA256 that leaves its result in place: -/// -/// * `combineLeaves(h, l0, l1)` — `h := SHA256(l0 ++ l1)` for two 32-byte leaf -/// blobs, straight from the IV. -/// * `combineNodes(a, b)` — `a := SHA256(a ++ b)` for two finished child -/// digests, in place; `b` is consumed. `a` "moves up a level." -/// -/// Both REQUIRE a closed hasher and leave it closed, so a finished hasher is -/// immediately reusable — no `reset` anywhere, no `Blob` for any internal node. -/// -/// === Single-SHA vs double-SHA trees === -/// -/// `combineLeaves`/`combineNodes` are one SHA256 per node, each computing -/// `SHA256(left ++ right)` — a plain single-SHA Merkle tree. For a Bitcoin-style -/// DOUBLE-SHA tree, call `fold(h)` after each combine — `fold` re-hashes the -/// node's own digest, so `combine… + fold` = `SHA256(SHA256(…))`. This example -/// does the double-SHA Bitcoin tree; for the plain single-SHA tree, delete the -/// two `fold` lines. -/// -/// Note: this is NOT RFC 6962. That tree prepends a domain-separation byte -/// (`0x00` to leaf data, `0x01` to internal nodes), so an internal node hashes -/// 65 bytes — `SHA256(0x01 || left || right)` — which these fixed 64-byte -/// combiners can't produce. -/// -/// === The algorithm: a Merkle-mountain-range peak stack === -/// -/// Walk the leaves left to right, two at a time, keeping a STACK of "peaks" — -/// completed subtrees still waiting for a right sibling. `hasher[j]` holds the -/// j-th peak and `level[j]` its tree level; `i` is the stack height. -/// -/// 1. Push each leaf pair as a node (`combineLeaves` + `fold`), a level-1 peak. -/// An unpaired final leaf is paired with itself (Bitcoin's duplication). -/// 2. While the new peak has the SAME level as the peak below it, merge the -/// two (`combineNodes` + `fold` into the lower peak) and pop — exactly like -/// carrying in binary addition. -/// 3. At the end, a power-of-two tree has one peak (the root). Otherwise -/// several peaks remain at decreasing levels; collapse them Bitcoin-style -/// by duplicating the lowest peak (`combineNodes` with itself, raising it a -/// level) and carrying, until one peak is left. -/// -/// A tree of N leaves needs only ⌈log2 N⌉ hashers. No recursion, no free-list. -/// -/// === What YOU must do to stay allocation-free === -/// -/// 1. Allocate the hashers ONCE, up front, and `close()` them so they start -/// in the closed state the combine primitives require. Never `Sha256.new()` -/// per node. -/// 2. Leaves must be 32-byte blobs (Merkle leaves are hashes, so this is the -/// normal case). -/// 3. Read the output with `readSum()` exactly once, for the root — that is -/// the only allocation. - -// In your own application, depend on the sha2 package and import it by name: -// import Sha256 "mo:sha2/Sha256"; -// These files live inside the sha2 repo, so they import the source directly. -import Sha256 "../src/Sha256"; -import Array "mo:core/Array"; -import VarArray "mo:core/VarArray"; - -module { - /// Bitcoin-style (double-SHA256) Merkle root of `leaves`, for ANY leaf count - /// (>= 1). Each leaf must be 32 bytes. Allocates nothing per node — only the - /// returned root `Blob`. - /// - /// Bitcoin's rule for non-power-of-two trees: whenever a level has an odd - /// number of nodes, the LAST node is duplicated (hashed with itself). The peak - /// stack handles this in two places — an unpaired final leaf is paired with - /// itself, and at the end any lone leftover peak is duplicated and carried up. - public func bitcoinMerkleRoot(leaves : [Blob]) : Blob { - let n = leaves.size(); - assert n >= 1; - - // A single leaf: by the Bitcoin convention the root is the leaf itself. - if (n == 1) return leaves[0]; - - // A pool of hashers, started CLOSED (combineLeaves/combineNodes both require - // a closed hasher). `hasher[0 .. i-1]` is the peak stack and `level[j]` the - // level of `hasher[j]`. `ceil(log2 n) + 2` slots is always enough (the peak - // stack plus the collapse, which can push the root one extra level). - var cap = 0; - var m = 1; - while (m < n) { m *= 2; cap += 1 }; // cap = ceil(log2 n) - cap += 2; - let hasher = Array.tabulate(cap, func(_) { let h = Sha256.new(); h.close(); h }); - let level = VarArray.repeat(0, cap); - - var i = 0; // stack height - var p = 0; // next leaf - while (p < n) { - // Push a leaf node. An unpaired final leaf is duplicated (Bitcoin's rule). - // The `fold` makes the node double-SHA; drop it for a single-SHA tree. - let right = if (p + 1 < n) leaves[p + 1] else leaves[p]; - hasher[i].combineLeaves(leaves[p], right); // SHA256(l0 ++ l1) - hasher[i].fold(); // -> double-SHA - level[i] := 1; - // Carry: while the top peak matches the level of the one below it, merge. - while (i > 0 and level[i - 1] == level[i]) { - hasher[i - 1].combineNodes(hasher[i]); // lower := SHA256(lower ++ top) - hasher[i - 1].fold(); // -> double-SHA (drop for single-SHA) - level[i - 1] += 1; // it rose a level - i -= 1; // pop the top - }; - i += 1; - p += 2; - }; - - // Collapse leftover peaks. A power-of-two tree already has one peak; for - // any other count the stack holds several peaks at strictly decreasing - // levels. Bitcoin duplicates the lone node at each odd level, which here is: - // duplicate the lowest peak (combine it with itself, raising it a level) and - // carry, repeating until a single peak — the root — remains. - while (i > 1) { - hasher[i - 1].combineNodes(hasher[i - 1]); // duplicate: SHA256(peak ++ peak) - hasher[i - 1].fold(); // -> double-SHA (drop for single-SHA) - level[i - 1] += 1; - while (i > 1 and level[i - 2] == level[i - 1]) { - hasher[i - 2].combineNodes(hasher[i - 1]); - hasher[i - 2].fold(); - level[i - 2] += 1; - i -= 1; - }; - }; - - // readSum is the one and only allocation. - Sha256.readSum(hasher[0]); - }; - - // --- Variations you can make in your own tree --- - // - // * Plain single-SHA tree: delete the `fold` lines — every node is then one - // `SHA256(left ++ right)`. (Not RFC 6962 — see the header. And note the - // last-node duplication above is specifically Bitcoin's rule; other trees - // handle odd levels differently.) - // - // * Caveat: duplicating the last node is the source of Bitcoin's CVE-2012-2459 - // (two distinct leaf lists can yield the same root); callers must reject - // blocks with duplicate txids in that position. -}; diff --git a/.mops/sha2@0.2.5/examples/NFold.mo b/.mops/sha2@0.2.5/examples/NFold.mo deleted file mode 100644 index dbe0c01..0000000 --- a/.mops/sha2@0.2.5/examples/NFold.mo +++ /dev/null @@ -1,84 +0,0 @@ -/// Example: N-fold hashing (apply SHA repeatedly) with `mo:sha2`, allocation-free. -/// -/// "N-fold" SHA256 means hashing the message, then hashing that digest, and so -/// on, N times: H^N(msg) = H(H(...H(msg))). N = 2 is the double SHA used by -/// Bitcoin (also available directly as `sumDouble`). -/// -/// The tools are `close()` and `fold()`: -/// * `close()` finalizes the message, leaving the digest H1 in the state. -/// * `fold()` hashes the digest currently in the state (state -> SHA256(state)) -/// in one specialized block, without ever producing a `Blob`. -/// * `readSum()` reads the finalized state out as a `Blob` (idempotent). -/// -/// So a clean N-fold is: -/// -/// digest.writeBlob(msg); -/// digest.close(); // H1 = SHA256(msg) -/// digest.fold(); // repeat (N - 1) times: H2, H3, ... HN -/// digest.readSum(); // read HN -> the result Blob -/// -/// Only `readSum()` allocates (the digest you return). `close()` and every -/// `fold()` are allocation-free. -/// -/// === Allocation-free for BULK hashing === -/// -/// When you hash MANY messages (or many items), the trap is allocating a fresh -/// hasher per item. Instead: -/// -/// 1. Allocate ONE hasher up front, outside the loop. -/// 2. `reset()` it before each message — this rewinds it for reuse instead of -/// allocating a new one. -/// 3. Feed input with `writeBlob` (allocation-free for word-aligned data). -/// 4. `close()` once, then `fold()` for the remaining rounds, and `readSum()` -/// to read each message's final digest. -/// -/// The only unavoidable allocations are the output digests themselves (one -/// `Blob` per message — the result you asked for). See `nfoldBatch` below. -/// -/// Note: `fold()` is sha256-only (its fast block is size-specific), so these -/// examples use the default `#sha256` algorithm. - -// In your own application, depend on the sha2 package and import it by name: -// import Sha256 "mo:sha2/Sha256"; -// These files live inside the sha2 repo, so they import the source directly. -import Sha256 "../src/Sha256"; -import Array "mo:core/Array"; - -module { - /// H^n(message): apply SHA256 `n` times (n >= 1). Allocates only the result. - public func nfold(message : Blob, n : Nat) : Blob { - assert n >= 1; - let h = Sha256.new(); - h.writeBlob(message); - h.close(); // H1 = SHA256(message) - var k = 1; - while (k < n) { - h.fold(); // an inner round; runs (n - 1) times - k += 1; - }; - h.readSum(); // read the final hash -> the result Blob - }; - - /// N-fold-hash a whole batch of messages while reusing a single hasher, so - /// the only allocations are the returned digests (one `Blob` per message). - public func nfoldBatch(messages : [Blob], n : Nat) : [Blob] { - assert n >= 1; - let h = Sha256.new(); // allocated ONCE for the entire batch - // Array.tabulate calls the function for index 0, 1, 2, ... in order, so it - // is safe to share and rewind the one hasher between items. - Array.tabulate( - messages.size(), - func(j) { - h.reset(); // rewind for the next message — no `Sha256.new()` per item - h.writeBlob(messages[j]); - h.close(); - var k = 1; - while (k < n) { - h.fold(); - k += 1; - }; - h.readSum(); - }, - ); - }; -}; diff --git a/.mops/sha2@0.2.5/mops.toml b/.mops/sha2@0.2.5/mops.toml deleted file mode 100644 index 2b0d67b..0000000 --- a/.mops/sha2@0.2.5/mops.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "sha2" -version = "0.2.5" -description = "Optimized implementation of all SHA2 functions" -repository = "https://github.com/research-ag/sha2" -keywords = [ "hash", "sha256", "sha512", "sha224", "sha384" ] -license = "Apache-2.0" - -[dependencies] -core = "2.5.0" - -[dev-dependencies] -bench-helper = "0.0.3" - -[requirements] -moc = "1.0.0" - -[toolchain] -moc = "1.10.0" -wasmtime = "44.0.3" diff --git a/.mops/sha2@0.2.5/src/Sha256.mo b/.mops/sha2@0.2.5/src/Sha256.mo deleted file mode 100644 index f9d543d..0000000 --- a/.mops/sha2@0.2.5/src/Sha256.mo +++ /dev/null @@ -1,486 +0,0 @@ -/// Cycle-optimized Sha256 variants. -/// -/// Features: -/// -/// * Algorithms: `sha256`, `sha224` -/// * Input types: `Blob`, `[Nat8]`, `[var Nat8]`, `Iter`, -/// * `at : Nat -> Nat8` (unchecked accessor), -/// * `next : () -> Nat8` (unchecked reader) -/// * Output types: `Blob` -/// -/// ```motoko name=import -/// import Sha256 "mo:sha2/Sha256"; -/// ``` - -import { type Iter } "mo:core/Types"; -import { arrayToBlob } "mo:prim"; - -import Buffer "sha256/buffer"; -import State "sha256/state"; -import _Digest "sha256/digest"; -import Types "sha256/types"; - -module { - /// SHA256 algorithms. - public type Algorithm = { #sha224; #sha256 }; - - /// Default algorithm. - public let algo = #sha256; // default algorithm used as implicit argument - - /// Digest type (including the algorithm field) - /// As a static record it can be declared `stable`. - public type Digest = Types.Digest and { - algo : Algorithm; - }; - - /// Create a new SHA2 digest instance for the specified algorithm. - /// The digest can be used to incrementally hash data by calling write functions, - /// then finalized with `sum()`. - /// - /// If incremental hashing is not needed, consider using the convenience functions `fromBlob`, `fromArray`, etc. - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// digest.writeBlob("Hello"); - /// digest.writeBlob(" world"); - /// let hash = digest.sum(); - /// ``` - /// - /// After finalizing with `sum()` the digest is "closed", i.e. no more data can be written to it. - /// - /// The default algorithm is `#sha256`. To use `#sha224`, pass it as an explicit argument: - /// - /// ```motoko include=import - /// let digest = Sha256.new(#sha224); - /// ``` - public func new(algo : (implicit : Algorithm)) : Digest { - let buf = Buffer.new(); - switch (algo) { - case (#sha224) { - { - algo = #sha224; - state = [var 0xc105, 0x9ed8, 0x367c, 0xd507, 0x3070, 0xdd17, 0xf70e, 0x5939, 0xffc0, 0x0b31, 0x6858, 0x1511, 0x64f9, 0x8fa7, 0xbefa, 0x4fa4]; - buffer = buf; - var closed = false; - }; - }; - case (_) { - { - algo = #sha256; - state = [var 0x6a09, 0xe667, 0xbb67, 0xae85, 0x3c6e, 0xf372, 0xa54f, 0xf53a, 0x510e, 0x527f, 0x9b05, 0x688c, 0x1f83, 0xd9ab, 0x5be0, 0xcd19]; - buffer = buf; - var closed = false; - }; - }; - }; - }; - - /// Reset the digest state to start a new hash computation. - /// After reset, the digest can be reused to hash new data. - /// This works even if the digest was previously finalized (is closed). - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// digest.writeBlob("First message"); - /// let hash1 = digest.sum(); - /// digest.reset(); - /// digest.writeBlob("Second message"); - /// let hash2 = digest.sum(); - /// ``` - // Load the algorithm's initial hash value (IV) into `state`. - // Direct half-word assignment avoids allocating a literal array (the original - // `state.set([...])` allocated a fresh 16-element array and copied it through - // a `Nat.range` iterator — ~10x the cost, dominating short-message hashing). - // `switch` (not `algo == #sha224`) because variant `==` allocates per call. - func loadIV(s : Types.State, algo : Algorithm) { - // prettier-ignore - switch (algo) { - case (#sha224) { - s[0] := 0xc105; s[1] := 0x9ed8; s[2] := 0x367c; s[3] := 0xd507; - s[4] := 0x3070; s[5] := 0xdd17; s[6] := 0xf70e; s[7] := 0x5939; - s[8] := 0xffc0; s[9] := 0x0b31; s[10] := 0x6858; s[11] := 0x1511; - s[12] := 0x64f9; s[13] := 0x8fa7; s[14] := 0xbefa; s[15] := 0x4fa4; - }; - case (_) { - s[0] := 0x6a09; s[1] := 0xe667; s[2] := 0xbb67; s[3] := 0xae85; - s[4] := 0x3c6e; s[5] := 0xf372; s[6] := 0xa54f; s[7] := 0xf53a; - s[8] := 0x510e; s[9] := 0x527f; s[10] := 0x9b05; s[11] := 0x688c; - s[12] := 0x1f83; s[13] := 0xd9ab; s[14] := 0x5be0; s[15] := 0xcd19; - }; - }; - }; - - public func reset(self : Digest) { - self.buffer.reset(); - loadIV(self.state, self.algo); - self.closed := false; - }; - - /// Create an independent copy of the digest with the same internal state. - /// This allows to finalize one of the two copies with `sum()` and to keep writing more data to the other. - /// For example, one can obtain intermediate hashes like this. - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// digest.writeBlob("Hello"); - /// let clone = digest.clone(); - /// let intermediate = clone.sum(); - /// digest.writeBlob(" world"); - /// let final = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed. - public func clone(self : Digest) : Digest { - assert not self.closed; - { - algo = self.algo; - buffer = self.buffer.clone(); - state = self.state.clone(); - var closed = false; - }; - }; - - /// Write a `Blob` to the digest. - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// digest.writeBlob("Hello"); - /// digest.writeBlob(" world"); - /// let hash = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed. - public func writeBlob(self : Digest, data : Blob) : () = _Digest.writeBlob(self, data); - - /// Combine two closed digests in place: replace `self`'s digest with - /// `SHA256(self.digest ++ other.digest)`, so `self` moves "up one level" in a - /// Merkle tree while `other` is consumed (read-only; the caller frees it). - /// This is a single SHA256: for a double-SHA tree (e.g. Bitcoin) call `fold` - /// after; for a single-SHA tree (each node `SHA256(left ++ right)`) don't. - /// (This is plain concatenation — NOT RFC 6962, which prepends a 0x01 - /// domain-separation byte and so hashes 65 bytes.) Self-contained — no - /// `reset`/`close` needed, no message buffer, no intermediate `Blob`; `self` - /// stays closed. The internal-node counterpart of `combineLeaves` (together - /// they build a Merkle tree in O(log n) hashers — see `examples/Merkle.mo`). - /// - /// Traps if `self` or `other` is not closed. - public func combineNodes(self : Digest, other : Digest) : () = _Digest.combineNodes(self, other); - - /// Hash two 32-byte blobs into `self` as `SHA256(b1 ++ b2)`, from a length-0 - /// start — a single SHA256, leaving the result in `self` (closed). The leaf - /// counterpart of `combineNodes`; for a double-SHA tree follow with `fold`, - /// for a single-SHA tree don't. Self-contained — requires `self` already - /// closed, starts from the IV (no `reset`), runs the data block plus a - /// hard-coded padding block in one call. `b1` fills message words 0..7, `b2` - /// words 8..15. - /// - /// Traps if `self` is not closed, or if either blob is not 32 bytes. - public func combineLeaves(self : Digest, b1 : Blob, b2 : Blob) : () = _Digest.combineLeaves(self, b1, b2); - - /// Write a `[Nat8]` array to the digest. - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// digest.writeArray([72, 101, 108, 108, 111]); // "Hello" - /// digest.writeBlob(" world"); - /// let hash = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed. - public func writeArray(self : Digest, data : [Nat8]) : () = _Digest.writeArray(self, data); - - /// Write a `[var Nat8]` array to the digest. - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// let data : [var Nat8] = [var 72, 101, 108, 108, 111]; - /// digest.writeVarArray(data); - /// let hash = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed. - public func writeVarArray(self : Digest, data : [var Nat8]) : () = _Digest.writeVarArray(self, data); - - /// Write data from a positional accessor function. - /// Takes `len` bytes starting from the `start` index. - /// It it the responsibility of the caller to ensure that the accessor function - /// can provide valid data for all requested indices. - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// let data = [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]; - /// func accessor(i : Nat) : Nat8 = data[i]; - /// digest.writeAccessor(accessor, 0, 5); // "Hello" - /// digest.writeAccessor(accessor, 5, 6); // " world" - /// let hash = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed, or if `data` traps for any index in `[start, start + len)`. - public func writeAccessor(self : Digest, data : Nat -> Nat8, start : Nat, len : Nat) : () = _Digest.writeAccessor(self, data, start, len); - - /// Write data from a reader function. - /// Takes exactly `len` bytes by calling the reader function `len` times. - /// It it the responsibility of the caller to ensure that the reader function - /// can provide valid data for all requested bytes. - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// let data = [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]; - /// var pos = 0; - /// func reader() : Nat8 { let b = data[pos]; pos += 1; b }; - /// digest.writeReader(reader, 5); // "Hello" - /// digest.writeReader(reader, 6); // " world" - /// let hash = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed, or if `data` traps during any of the `len` calls. - public func writeReader(self : Digest, data : () -> Nat8, len : Nat) : () = _Digest.writeReader(self, data, len); - - /// Write data from an `Iter` to the digest. Consumes the entire iterator. - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// let iter = [72, 101, 108, 108, 111].vals(); - /// digest.writeIter(iter); // "Hello" - /// let hash = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed. - public func writeIter(self : Digest, data : Iter) : () = _Digest.writeIter(self, data.next); - - // Extract the state from a Digest as a [Nat8] array - func stateNat8(x : Digest) : [Nat8] = switch (x.algo) { - case (#sha224) x.state.toNat8Array(28); - case (#sha256) x.state.toNat8Array(32); - }; - - // Extract the state from a Digest as a Blob - func stateBlob(x : Digest) : Blob = arrayToBlob(stateNat8(x)); - - /// Finalize the digest and return the hash as a `Blob`. - /// This closes the digest. It cannot be used for anything again unless it is reset with the `reset()` function. - /// For example, attempting to write more data to it or finalizing it a second time will trap. - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// digest.writeBlob("Hello world"); - /// let hash : Blob = digest.sum(); - /// ``` - /// - /// Traps if `self` is already closed. - public func sum(self : Digest) : Blob { - _Digest.close(self); - return stateBlob(self); - }; - - /// Finalize the digest by writing padding, without returning the hash. After - /// `close()` the digest is closed; read the hash with `readSum()` (any number - /// of times) or hash it again with `fold()`. - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// digest.writeBlob("Hello world"); - /// digest.close(); - /// let hash : Blob = digest.readSum(); - /// ``` - /// - /// Traps if `self` is already closed. - public func close(self : Digest) : () = _Digest.close(self); - - /// Read the hash of a closed digest. Idempotent: unlike `sum()` it does not - /// finalize, so it can be called repeatedly after `close()`, `sum()`, or - /// `fold()`. - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// digest.writeBlob("Hello world"); - /// let once : Blob = digest.sum(); - /// let again : Blob = digest.readSum(); // == once - /// ``` - /// - /// Traps if `self` is not closed. - public func readSum(self : Digest) : Blob { - assert self.closed; - stateBlob(self); - }; - - /// Hash a closed digest's own hash, in place: replace the state with - /// `SHA256(state)`, leaving the digest closed. This is the building block for - /// N-fold and double SHA256: after `close()`, calling `fold()` (N-1) times - /// yields the N-fold hash, and a single `fold()` gives the double SHA256 used - /// by Bitcoin (see `sumDouble`). Allocation-free — the digest is hashed - /// straight from the state in one specialized block, no intermediate `Blob`. - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// digest.writeBlob("Hello world"); - /// digest.close(); - /// digest.fold(); - /// let doubleHash : Blob = digest.readSum(); - /// ``` - /// - /// Traps if `self` is not closed, or if `self` is a sha224 digest (sha224 - /// folding is not yet supported). - public func fold(self : Digest) { - assert self.closed; - assert (switch (self.algo) { case (#sha256) true; case (#sha224) false }); - State.process_fold_block(self.state); - }; - - /// Finalize the digest as a double SHA256, i.e. `sum(sum(message))`, and - /// return the hash as a `Blob`. This is the hash used by Bitcoin. - /// - /// ```motoko include=import - /// let digest = Sha256.new(); - /// digest.writeBlob("Hello world"); - /// let hash : Blob = digest.sumDouble(); - /// ``` - /// - /// Closes the digest. Traps if `self` is already closed, or if `self` is a - /// sha224 digest (folding is sha256-only for now). - public func sumDouble(self : Digest) : Blob { - _Digest.close(self); - fold(self); - readSum(self); - }; - - /// Directly calculate the SHA2 hash digest from a `Blob`. - /// This is a convenience function that creates a digest, writes the data, - /// and returns the final hash in one step. - /// - /// ```motoko include=import - /// let hash = Sha256.fromBlob("Hello world"); - /// ``` - /// - /// The default algorithm is `#sha256`. To use `#sha224`, pass it as an explicit first argument: - /// - /// ```motoko include=import - /// let hash = Sha256.fromBlob(#sha224, "Hello world"); - /// ``` - /// - /// Never traps. - public func fromBlob(algo : (implicit : Algorithm), data : Blob) : Blob { - let digest = new(algo); - digest.writeBlob(data); - return sum(digest); - }; - - /// Calculate the SHA2 hash digest from a `[Nat8]` array. - /// This is a convenience function that creates a digest, writes the data, - /// and returns the final hash in one step. - /// - /// ```motoko include=import - /// let data = [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]; - /// let hash = Sha256.fromArray(data); - /// ``` - /// - /// The default algorithm is `#sha256`. To use `#sha224`, pass it as an explicit first argument: - /// - /// ```motoko include=import - /// let hash = Sha256.fromArray(#sha224, data); - /// ``` - /// - /// Never traps. - public func fromArray(algo : (implicit : Algorithm), data : [Nat8]) : Blob { - let digest = new(algo); - digest.writeArray(data); - return sum(digest); - }; - - /// Calculate the SHA2 hash digest from a `[var Nat8]` array. - /// This is a convenience function that creates a digest, writes the data, - /// and returns the final hash in one step. - /// - /// ```motoko include=import - /// let data : [var Nat8] = [var 72, 101, 108, 108, 111]; - /// let hash = Sha256.fromVarArray(data); - /// ``` - /// - /// The default algorithm is `#sha256`. To use `#sha224`, pass it as an explicit first argument: - /// - /// ```motoko include=import - /// let hash = Sha256.fromVarArray(#sha224, data); - /// ``` - /// - /// Never traps. - public func fromVarArray(algo : (implicit : Algorithm), data : [var Nat8]) : Blob { - let digest = new(algo); - digest.writeVarArray(data); - return sum(digest); - }; - - /// Calculate the SHA2 hash digest from an entire `Iter`. - /// This is a convenience function that creates a digest, writes all data - /// from the iterator, and returns the final hash in one step. - /// - /// ```motoko include=import - /// let data = [72, 101, 108, 108, 111].vals(); - /// let hash = Sha256.fromIter(data); - /// ``` - /// - /// The default algorithm is `#sha256`. To use `#sha224`, pass it as an explicit first argument: - /// - /// ```motoko include=import - /// let hash = Sha256.fromIter(#sha224, data); - /// ``` - /// - /// Never traps. - public func fromIter(algo : (implicit : Algorithm), data : Iter) : Blob { - let digest = new(algo); - _Digest.writeIter(digest, data.next); - return sum(digest); - }; - - /// Calculate the SHA2 hash digest from a positional accessor function. - /// Takes `len` bytes counting from the `start` index. - /// It it the responsibility of the caller to ensure that the accessor function - /// can provide valid data for all requested indices. - /// This is a convenience function that creates a digest, writes the data, - /// and returns the final hash in one step. - /// - /// ```motoko include=import - /// let data = [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]; - /// func accessor(i : Nat) : Nat8 = data[i]; - /// let hash = Sha256.fromAccessor(accessor, 0, 5); - /// ``` - /// - /// The default algorithm is `#sha256`. To use `#sha224`, pass it as an explicit first argument: - /// - /// ```motoko include=import - /// let hash = Sha256.fromAccessor(#sha224, accessor, 0, 5); - /// ``` - /// - /// Does not trap unless user-provided accessor function `data` traps. - public func fromAccessor(algo : (implicit : Algorithm), data : Nat -> Nat8, start : Nat, len : Nat) : Blob { - let digest = new(algo); - digest.writeAccessor(data, start, len); - return sum(digest); - }; - - /// Calculate the SHA2 hash digest from a reader function. - /// Takes exactly `len` bytes by calling the reader function `len` times. - /// It it the responsibility of the caller to ensure that the reader function - /// can provide valid data for all requested bytes. - /// This is a convenience function that creates a digest, writes the data, - /// and returns the final hash in one step. - /// - /// ```motoko include=import - /// var pos = 0; - /// let data = [72, 101, 108, 108, 111]; - /// func reader() : Nat8 { let b = data[pos]; pos += 1; b }; - /// let hash = Sha256.fromReader(reader, 5); - /// ``` - /// - /// The default algorithm is `#sha256`. To use `#sha224`, pass it as an explicit first argument: - /// - /// ```motoko include=import - /// let hash = Sha256.fromReader(#sha224, reader, 5); - /// ``` - /// - /// Does not trap unless user-provided reader function `next` traps. - public func fromReader(algo : (implicit : Algorithm), data : () -> Nat8, len : Nat) : Blob { - let digest = new(algo); - digest.writeReader(data, len); - return sum(digest); - }; -}; diff --git a/.mops/sha2@0.2.5/src/Sha512.mo b/.mops/sha2@0.2.5/src/Sha512.mo deleted file mode 100644 index 19a5e9e..0000000 --- a/.mops/sha2@0.2.5/src/Sha512.mo +++ /dev/null @@ -1,519 +0,0 @@ -/// Cycle-optimized Sha512 variants. -/// -/// Features: -/// -/// * Algorithms: `sha512_224`, `sha512_256`, `sha384`, `sha512` -/// * Input types: `Blob`, `[Nat8]`, `[var Nat8]`, `Iter`, -/// * `at : Nat -> Nat8` (unchecked accessor), -/// * `next : () -> Nat8` (unchecked reader) -/// * Output types: `Blob` -/// -/// ```motoko name=import -/// import Sha512 "mo:sha2/Sha512"; -/// ``` - -import { type Iter } "mo:core/Types"; -import { arrayToBlob; explodeNat64 } "mo:prim"; -import VarArray "mo:core/VarArray"; -import _Digest "sha512/digest"; -import Types "sha512/types"; - -module { - /// SHA512 algorithms. - public type Algorithm = { - #sha384; - #sha512; - #sha512_224; - #sha512_256; - }; - - /// Default algorithm. - public let algo = #sha512; // default algorithm used as implicit argument - - /// Digest type (including the algorithm field) - /// As a static record it can be declared `stable`. - public type Digest = Types.Digest and { - algo : Algorithm; - }; - - let ivs : [[Nat64]] = [ - [ - // 512-224 - 0x8c3d37c819544da2, - 0x73e1996689dcd4d6, - 0x1dfab7ae32ff9c82, - 0x679dd514582f9fcf, - 0x0f6d2b697bd44da8, - 0x77e36f7304c48942, - 0x3f9d85a86a1d36c8, - 0x1112e6ad91d692a1, - ], - [ - // 512-256 - 0x22312194fc2bf72c, - 0x9f555fa3c84c64c2, - 0x2393b86b6f53b151, - 0x963877195940eabd, - 0x96283ee2a88effe3, - 0xbe5e1e2553863992, - 0x2b0199fc2c85b8aa, - 0x0eb72ddc81c52ca2, - ], - [ - // 384 - 0xcbbb9d5dc1059ed8, - 0x629a292a367cd507, - 0x9159015a3070dd17, - 0x152fecd8f70e5939, - 0x67332667ffc00b31, - 0x8eb44a8768581511, - 0xdb0c2e0d64f98fa7, - 0x47b5481dbefa4fa4, - ], - [ - // 512 - 0x6a09e667f3bcc908, - 0xbb67ae8584caa73b, - 0x3c6ef372fe94f82b, - 0xa54ff53a5f1d36f1, - 0x510e527fade682d1, - 0x9b05688c2b3e6c1f, - 0x1f83d9abfb41bd6b, - 0x5be0cd19137e2179, - ], - ]; - - /// Create a new SHA2 digest instance for the specified algorithm. - /// The digest can be used to incrementally hash data by calling write functions, - /// then finalized with `sum()`. - /// - /// If incremental hashing is not needed, consider using the convenience functions `fromBlob`, `fromArray`, etc. - /// - /// ```motoko include=import - /// let digest = Sha512.new(); - /// digest.writeBlob("Hello"); - /// digest.writeBlob(" world"); - /// let hash = digest.sum(); - /// ``` - /// - /// After finalizing with `sum()` the digest is "closed", i.e. no more data can be written to it. - /// - /// The default algorithm is `#sha512`. To use `#sha384`, `#sha512_256` or `#sha512_224`, pass it as an explicit argument: - /// - /// ```motoko include=import - /// let digest = Sha512.new(#sha384); - /// ``` - public func new(algo : (implicit : Algorithm)) : Digest { - { - algo; - msg : [var Nat64] = [var 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - var i_msg : Nat8 = 0; - var i_byte : Nat8 = 8; - var i_block : Nat64 = 0; - var word : Nat64 = 0; - s : [var Nat64] = switch (algo) { - case (#sha512_224) [var 0x8c3d37c819544da2, 0x73e1996689dcd4d6, 0x1dfab7ae32ff9c82, 0x679dd514582f9fcf, 0x0f6d2b697bd44da8, 0x77e36f7304c48942, 0x3f9d85a86a1d36c8, 0x1112e6ad91d692a1]; - case (#sha512_256) [var 0x22312194fc2bf72c, 0x9f555fa3c84c64c2, 0x2393b86b6f53b151, 0x963877195940eabd, 0x96283ee2a88effe3, 0xbe5e1e2553863992, 0x2b0199fc2c85b8aa, 0x0eb72ddc81c52ca2]; - case (#sha384) [var 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939, 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4]; - case (#sha512) [var 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179]; - }; - var closed = false; - }; - }; - - // Load the algorithm's initial hash value (IV) into the state. - // Unrolled copy avoids allocating the `[0..7]` index array and its iterator - // on every reset (cf. the Sha256 reset optimization). - func loadIV(self : Digest) { - let i = switch (self.algo) { - case (#sha512_224) 0; - case (#sha512_256) 1; - case (#sha384) 2; - case (#sha512) 3; - }; - let v = ivs[i]; - // prettier-ignore - do { - self.s[0] := v[0]; self.s[1] := v[1]; self.s[2] := v[2]; self.s[3] := v[3]; - self.s[4] := v[4]; self.s[5] := v[5]; self.s[6] := v[6]; self.s[7] := v[7]; - }; - }; - - /// Reset the digest state to start a new hash computation. - /// After reset, the digest can be reused to hash new data. - /// This works even if the digest was previously finalized (is closed). - /// - /// ```motoko include=import - /// let digest = Sha512.new(); - /// digest.writeBlob("First message"); - /// let hash1 = digest.sum(); - /// digest.reset(); - /// digest.writeBlob("Second message"); - /// let hash2 = digest.sum(); - /// ``` - public func reset(self : Digest) { - self.i_msg := 0; - self.i_byte := 8; - self.i_block := 0; - loadIV(self); - self.closed := false; - }; - - /// Create an independent copy of the digest with the same internal state. - /// This allows to finalize one of the two copies with `sum()` and to keep writing more data to the other. - /// For example, one can obtain intermediate hashes like this. - /// - /// ```motoko include=import - /// let digest = Sha512.new(); - /// digest.writeBlob("Hello"); - /// let clone = digest.clone(); - /// let intermediate = clone.sum(); - /// digest.writeBlob(" world"); - /// let final = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed. - public func clone(self : Digest) : Digest { - assert not self.closed; - { - algo = self.algo; - msg = VarArray.clone(self.msg); - var word = self.word; - var i_msg = self.i_msg; - var i_byte = self.i_byte; - var i_block = self.i_block; - s = self.s.clone(); - var closed = false; - }; - }; - - /// Write a `Blob` to the digest. - /// - /// ```motoko include=import - /// let digest = Sha512.new(); - /// digest.writeBlob("Hello"); - /// digest.writeBlob(" world"); - /// let hash = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed. - public func writeBlob(self : Digest, data : Blob) : () = _Digest.writeBlob(self, data); - - /// Write a `[Nat8]` array to the digest. - /// - /// ```motoko include=import - /// let digest = Sha512.new(); - /// digest.writeArray([72, 101, 108, 108, 111]); // "Hello" - /// digest.writeBlob(" world"); - /// let hash = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed. - public func writeArray(self : Digest, data : [Nat8]) : () = _Digest.writeArray(self, data); - - /// Write a `[var Nat8]` array to the digest. - /// - /// ```motoko include=import - /// let digest = Sha512.new(); - /// let data : [var Nat8] = [var 72, 101, 108, 108, 111]; - /// digest.writeVarArray(data); - /// let hash = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed. - public func writeVarArray(self : Digest, data : [var Nat8]) : () = _Digest.writeVarArray(self, data); - - /// Write data from a positional accessor function. - /// Takes `len` bytes starting from the `start` index. - /// It it the responsibility of the caller to ensure that the accessor function - /// can provide valid data for all requested indices. - /// - /// ```motoko include=import - /// let digest = Sha512.new(); - /// let data = [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]; - /// func accessor(i : Nat) : Nat8 = data[i]; - /// digest.writeAccessor(accessor, 0, 5); // "Hello" - /// digest.writeAccessor(accessor, 5, 6); // " world" - /// let hash = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed, or if `at` traps for any index in `[start, start + len)`. - public func writeAccessor(self : Digest, at : Nat -> Nat8, start : Nat, len : Nat) : () = _Digest.writeAccessor(self, at, start, len); - - /// Write data from a reader function. - /// Takes exactly `len` bytes by calling the reader function `len` times. - /// It it the responsibility of the caller to ensure that the reader function - /// can provide valid data for all requested bytes. - /// - /// ```motoko include=import - /// let digest = Sha512.new(); - /// let data = [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]; - /// var pos = 0; - /// func reader() : Nat8 { let b = data[pos]; pos += 1; b }; - /// digest.writeReader(reader, 5); // "Hello" - /// digest.writeReader(reader, 6); // " world" - /// let hash = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed, or if `next` traps during any of the `len` calls. - public func writeReader(self : Digest, next : () -> Nat8, len : Nat) : () = _Digest.writeReader(self, next, len); - - /// Write data from an `Iter` to the digest. Consumes the entire iterator. - /// - /// ```motoko include=import - /// let digest = Sha512.new(); - /// let iter = [72, 101, 108, 108, 111].vals(); - /// digest.writeIter(iter); // "Hello" - /// let hash = digest.sum(); - /// ``` - /// - /// Traps if `self` is closed. - public func writeIter(self : Digest, data : Iter) : () = _Digest.writeIter(self, data.next); - - /// Finalize the digest and return the hash as a `Blob`. - /// This closes the digest. It cannot be used for anything again unless it is reset with the `reset()` function. - /// For example, attempting to write more data to it or finalizing it a second time will trap. - /// - /// ```motoko include=import - /// let digest = Sha512.new(); - /// digest.writeBlob("Hello world"); - /// let hash : Blob = digest.sum(); - /// ``` - /// - /// Traps if `self` is already closed. - public func sum(self : Digest) : Blob { - _Digest.close(self); - stateBlob(self); - }; - - /// Finalize the digest by writing padding, without returning the hash. After - /// `close()` the digest is closed; read the hash with `readSum()` (any number - /// of times). - /// - /// ```motoko include=import - /// let digest = Sha512.new(); - /// digest.writeBlob("Hello world"); - /// digest.close(); - /// let hash : Blob = digest.readSum(); - /// ``` - /// - /// Traps if `self` is already closed. - public func close(self : Digest) : () = _Digest.close(self); - - /// Read the hash of a closed digest. Idempotent: unlike `sum()` it does not - /// finalize, so it can be called repeatedly after `close()` or `sum()`. - /// - /// ```motoko include=import - /// let digest = Sha512.new(); - /// digest.writeBlob("Hello world"); - /// let once : Blob = digest.sum(); - /// let again : Blob = digest.readSum(); // == once - /// ``` - /// - /// Traps if `self` is not closed. - public func readSum(self : Digest) : Blob { - assert self.closed; - stateBlob(self); - }; - - func stateBlob(x : Digest) : Blob { - let (d0, d1, d2, d3, d4, d5, d6, d7) = explodeNat64(x.s[0]); - let (d8, d9, d10, d11, d12, d13, d14, d15) = explodeNat64(x.s[1]); - let (d16, d17, d18, d19, d20, d21, d22, d23) = explodeNat64(x.s[2]); - let (d24, d25, d26, d27, d28, d29, d30, d31) = explodeNat64(x.s[3]); - - // `switch` (not `== #...`) because variant `==` allocates per call. Longer - // digests explode the extra state words lazily inside their cases. - switch (x.algo) { - case (#sha512_224) { - // prettier-ignore - arrayToBlob([ - d0, d1, d2, d3, d4, d5, d6, d7, - d8, d9, d10, d11, d12, d13, d14, d15, - d16, d17, d18, d19, d20, d21, d22, d23, - d24, d25, d26, d27 - ]); - }; - case (#sha512_256) { - // prettier-ignore - arrayToBlob([ - d0, d1, d2, d3, d4, d5, d6, d7, - d8, d9, d10, d11, d12, d13, d14, d15, - d16, d17, d18, d19, d20, d21, d22, d23, - d24, d25, d26, d27, - d28, d29, d30, d31 - ]); - }; - case (#sha384) { - let (d32, d33, d34, d35, d36, d37, d38, d39) = explodeNat64(x.s[4]); - let (d40, d41, d42, d43, d44, d45, d46, d47) = explodeNat64(x.s[5]); - // prettier-ignore - arrayToBlob([ - d0, d1, d2, d3, d4, d5, d6, d7, - d8, d9, d10, d11, d12, d13, d14, d15, - d16, d17, d18, d19, d20, d21, d22, d23, - d24, d25, d26, d27, d28, d29, d30, d31, - d32, d33, d34, d35, d36, d37, d38, d39, - d40, d41, d42, d43, d44, d45, d46, d47 - ]); - }; - case (#sha512) { - let (d32, d33, d34, d35, d36, d37, d38, d39) = explodeNat64(x.s[4]); - let (d40, d41, d42, d43, d44, d45, d46, d47) = explodeNat64(x.s[5]); - let (d48, d49, d50, d51, d52, d53, d54, d55) = explodeNat64(x.s[6]); - let (d56, d57, d58, d59, d60, d61, d62, d63) = explodeNat64(x.s[7]); - // prettier-ignore - arrayToBlob([ - d0, d1, d2, d3, d4, d5, d6, d7, - d8, d9, d10, d11, d12, d13, d14, d15, - d16, d17, d18, d19, d20, d21, d22, d23, - d24, d25, d26, d27, d28, d29, d30, d31, - d32, d33, d34, d35, d36, d37, d38, d39, - d40, d41, d42, d43, d44, d45, d46, d47, - d48, d49, d50, d51, d52, d53, d54, d55, - d56, d57, d58, d59, d60, d61, d62, d63 - ]); - }; - }; - }; - - /// Directly calculate the SHA2 hash digest from a `Blob`. - /// This is a convenience function that creates a digest, writes the data, - /// and returns the final hash in one step. - /// - /// ```motoko include=import - /// let hash = Sha512.fromBlob("Hello world"); - /// ``` - /// - /// The default algorithm is `#sha512`. To use `#sha384`, `#sha512_256` or `#sha512_224`, pass it as an explicit argument: - /// - /// ```motoko include=import - /// let hash = Sha512.fromBlob(#sha384, "Hello world"); - /// ``` - /// - /// Never traps. - public func fromBlob(algo : (implicit : Algorithm), b : Blob) : Blob { - let d = new(algo); - d.writeBlob(b); - return sum(d); - }; - - /// Calculate the SHA2 hash digest from a `[Nat8]` array. - /// This is a convenience function that creates a digest, writes the data, - /// and returns the final hash in one step. - /// - /// ```motoko include=import - /// let data = [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]; - /// let hash = Sha512.fromArray(data); - /// ``` - /// - /// The default algorithm is `#sha512`. To use `#sha384`, `#sha512_256` or `#sha512_224`, pass it as an explicit argument: - /// - /// ```motoko include=import - /// let hash = Sha512.fromArray(#sha384, data); - /// ``` - /// - /// Never traps. - public func fromArray(algo : (implicit : Algorithm), arr : [Nat8]) : Blob { - let d = new(algo); - d.writeArray(arr); - return sum(d); - }; - - /// Calculate the SHA2 hash digest from a `[var Nat8]` array. - /// This is a convenience function that creates a digest, writes the data, - /// and returns the final hash in one step. - /// - /// ```motoko include=import - /// let data : [var Nat8] = [var 72, 101, 108, 108, 111]; - /// let hash = Sha512.fromVarArray(data); - /// ``` - /// - /// The default algorithm is `#sha512`. To use `#sha384`, `#sha512_256` or `#sha512_224`, pass it as an explicit argument: - /// - /// ```motoko include=import - /// let hash = Sha512.fromVarArray(#sha384, data); - /// ``` - /// - /// Never traps. - public func fromVarArray(algo : (implicit : Algorithm), arr : [var Nat8]) : Blob { - let d = new(algo); - d.writeVarArray(arr); - return sum(d); - }; - - /// Calculate the SHA2 hash digest from an entire `Iter`. - /// This is a convenience function that creates a digest, writes all data - /// from the iterator, and returns the final hash in one step. - /// - /// ```motoko include=import - /// let data = [72, 101, 108, 108, 111].vals(); - /// let hash = Sha512.fromIter(data); - /// ``` - /// - /// The default algorithm is `#sha512`. To use `#sha384`, `#sha512_256` or `#sha512_224`, pass it as an explicit argument: - /// - /// ```motoko include=import - /// let hash = Sha512.fromIter(#sha384, data); - /// ``` - /// - /// Never traps. - public func fromIter(algo : (implicit : Algorithm), iter : Iter) : Blob { - let d = new(algo); - _Digest.writeIter(d, iter.next); - return sum(d); - }; - - /// Calculate the SHA2 hash digest from a positional accessor function. - /// Takes `len` bytes counting from the `start` index. - /// It it the responsibility of the caller to ensure that the accessor function - /// can provide valid data for all requested indices. - /// This is a convenience function that creates a digest, writes the data, - /// and returns the final hash in one step. - /// - /// ```motoko include=import - /// let data = [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]; - /// func accessor(i : Nat) : Nat8 = data[i]; - /// let hash = Sha512.fromAccessor(accessor, 0, 5); - /// ``` - /// - /// The default algorithm is `#sha512`. To use `#sha384`, `#sha512_256` or `#sha512_224`, pass it as an explicit argument: - /// - /// ```motoko include=import - /// let hash = Sha512.fromAccessor(#sha384, accessor, 0, 5); - /// ``` - /// - /// Does not trap unless user-provided accessor function `data` traps. - public func fromAccessor(algo : (implicit : Algorithm), data : Nat -> Nat8, start : Nat, len : Nat) : Blob { - let d = new(algo); - d.writeAccessor(data, start, len); - return sum(d); - }; - - /// Calculate the SHA2 hash digest from a reader function. - /// Takes exactly `len` bytes by calling the reader function `len` times. - /// It it the responsibility of the caller to ensure that the reader function - /// can provide valid data for all requested bytes. - /// This is a convenience function that creates a digest, writes the data, - /// and returns the final hash in one step. - /// - /// ```motoko include=import - /// var pos = 0; - /// let data = [72, 101, 108, 108, 111]; - /// func reader() : Nat8 { let b = data[pos]; pos += 1; b }; - /// let hash = Sha512.fromReader(reader, 5); - /// ``` - /// - /// The default algorithm is `#sha512`. To use `#sha384`, `#sha512_256` or `#sha512_224`, pass it as an explicit argument: - /// - /// ```motoko include=import - /// let hash = Sha512.fromReader(#sha384, reader, 5); - /// ``` - /// - /// Does not trap unless user-provided reader function `next` traps. - public func fromReader(algo : (implicit : Algorithm), next : () -> Nat8, len : Nat) : Blob { - let d = new(algo); - d.writeReader(next, len); - return sum(d); - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha256/buffer/lib.mo b/.mops/sha2@0.2.5/src/sha256/buffer/lib.mo deleted file mode 100644 index 16e337a..0000000 --- a/.mops/sha2@0.2.5/src/sha256/buffer/lib.mo +++ /dev/null @@ -1,168 +0,0 @@ -/// SHA256 message buffer operations. - -import VarArray "mo:core/VarArray"; -import Prim "mo:prim"; -import { type Buffer } "../types"; - -module { - - /// Create a new empty buffer. - public func new() : Buffer = { - msg : [var Nat16] = [var 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - var i_msg : Nat8 = 0; - var i_block : Nat32 = 0; - var high : Bool = true; - var word : Nat16 = 0; - }; - - /// Reset the buffer state. - public func reset(self : Buffer) { - self.i_msg := 0; - self.i_block := 0; - self.high := true; - }; - - /// Create an independent copy of the buffer. - public func clone(self : Buffer) : Buffer = { - msg = VarArray.clone(self.msg); - var i_msg = self.i_msg; - var i_block = self.i_block; - var high = self.high; - var word = self.word; - }; - - let nat8To16 = Prim.nat8ToNat16; - let nat8ToNat = Prim.nat8ToNat; - - /* - private func writeByte(x : Digest, val : Nat8) : () { - if (x.high) { - x.word := nat8To16(val) << 8; - x.high := false; - } else { - x.msg[nat8ToNat(x.i_msg)] := x.word ^ nat8To16(val); - x.i_msg +%= 1; - x.high := true; - }; - if (x.i_msg == 32) { - x.state.process_block_from_msg_buffer(x.msg); - x.i_msg := 0; - x.i_block +%= 1; - }; - }; - */ - - /// Load bytes obtained via `at(i)` for `i` in `[start, sz)` into the SHA256 message buffer, stopping when the buffer fills (32 words) or when `i` reaches `sz`. - /// Returns the index just past the last byte consumed. - public func load_chunk(self : Buffer, at : Nat -> Nat8, sz : Nat, start : Nat) : (end : Nat) { - if (start >= sz) return start; - var i = start; - let msg = self.msg; - var i_msg = self.i_msg; - if (not self.high) { - msg[nat8ToNat(i_msg)] := self.word ^ nat8To16(at(i)); - i_msg +%= 1; - self.high := true; - i += 1; - if (i_msg == 32) { - self.i_msg := i_msg; - return i; - }; - }; - let i_max : Nat = i + ((sz - i) / 2) * 2; - // Note: setting i_max always to sz - 1 also works (only for multiples of 2). - while (i < i_max) { - msg[nat8ToNat(i_msg)] := nat8To16(at(i)) << 8 ^ nat8To16(at(i + 1)); - i_msg +%= 1; - i += 2; - if (i_msg == 32) { - self.i_msg := i_msg; - return i; - }; - }; - while (i < sz) { - if (self.high) { - self.word := nat8To16(at(i)) << 8; - self.high := false; - } else { - msg[nat8ToNat(i_msg)] := self.word ^ nat8To16(at(i)); - i_msg +%= 1; - self.high := true; - }; - i += 1; - }; - self.i_msg := i_msg; - return i; - }; - - /// Like `load_chunk`, but reads directly from a `Blob` instead of through an - /// accessor function — avoids allocating a closure on every call. - public func load_chunk_blob(self : Buffer, data : Blob, sz : Nat, start : Nat) : (end : Nat) { - if (start >= sz) return start; - var i = start; - let msg = self.msg; - var i_msg = self.i_msg; - if (not self.high) { - msg[nat8ToNat(i_msg)] := self.word ^ nat8To16(data[i]); - i_msg +%= 1; - self.high := true; - i += 1; - if (i_msg == 32) { - self.i_msg := i_msg; - return i; - }; - }; - let i_max : Nat = i + ((sz - i) / 2) * 2; - while (i < i_max) { - msg[nat8ToNat(i_msg)] := nat8To16(data[i]) << 8 ^ nat8To16(data[i + 1]); - i_msg +%= 1; - i += 2; - if (i_msg == 32) { - self.i_msg := i_msg; - return i; - }; - }; - while (i < sz) { - if (self.high) { - self.word := nat8To16(data[i]) << 8; - self.high := false; - } else { - msg[nat8ToNat(i_msg)] := self.word ^ nat8To16(data[i]); - i_msg +%= 1; - self.high := true; - }; - i += 1; - }; - self.i_msg := i_msg; - return i; - }; - - /// Load bytes pulled from the iterator `next` into the SHA256 message buffer, stopping when the buffer fills (32 words) or when `next` returns `null`. - public func load_iter(self : Buffer, next : () -> ?Nat8) { - let msg = self.msg; - var i_msg = self.i_msg; - if (not self.high) { - let ?val = next() else return; - msg[nat8ToNat(i_msg)] := self.word ^ nat8To16(val); - i_msg +%= 1; - self.high := true; - }; - - while (i_msg < 32) { - let ?val0 = next() else { - self.i_msg := i_msg; - return; - }; - let ?val1 = next() else { - // high must be true here - self.word := nat8To16(val0) << 8; - self.high := false; - self.i_msg := i_msg; - return; - }; - msg[nat8ToNat(i_msg)] := nat8To16(val0) << 8 ^ nat8To16(val1); - i_msg +%= 1; - }; - self.i_msg := i_msg; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha256/digest/lib.mo b/.mops/sha2@0.2.5/src/sha256/digest/lib.mo deleted file mode 100644 index ac5056c..0000000 --- a/.mops/sha2@0.2.5/src/sha256/digest/lib.mo +++ /dev/null @@ -1,241 +0,0 @@ -/// SHA-256 digest module -/// -/// Handles writing data to the digest and closing it with padding. -/// The functions in this module orchestrate: -/// * writing data to the digest's internal buffer -/// * processing the internal buffer to update the digest state -/// * processing full blocks of data directly from the input when possible -/// -/// Methods: -/// writeBlob, writeArray, writeVarArray, writeAccessor, writeReader, writeIter, -/// writePadding, close - -import Prim "mo:prim"; - -import _Buffer "../buffer"; -import _State "../state"; -import _ProcessMsg "../state/process/blocks/iter"; // state.process_blocks - -import { type Digest } "../types"; -import { type State } "../types"; - -module { - let natToNat32 = Prim.natToNat32; - let nat8ToNat = Prim.nat8ToNat; - let nat8To16 = Prim.nat8ToNat16; - let nat32To64 = Prim.nat32ToNat64; - let intToNat64Wrap = Prim.intToNat64Wrap; - - func writeData(x : Digest, data : Nat -> Nat8, sz : Nat, start : Nat, process_blocks : Nat -> Nat) { - assert not x.closed; - if (sz == start) return; - let (buf, state) = (x.buffer, x.state); - var pos = start; - if (buf.i_msg > 0 or not buf.high) { - pos := buf.load_chunk(data, sz, start); - if (buf.i_msg == 32) { - state.process_block_from_msg(buf.msg); - buf.i_msg := 0; - buf.i_block +%= 1; - }; - }; - // if (buf.i_msg != 0) return; - let end = process_blocks(pos); - buf.i_block +%= natToNat32(end - pos) / 64; - ignore buf.load_chunk(data, sz, end); - if (buf.i_msg == 32) { - state.process_block_from_msg(buf.msg); - buf.i_msg := 0; - buf.i_block +%= 1; - }; - }; - - /// Write a `Blob` to the digest. - /// Traps if `self` is closed. - /// - /// Specialized for `Blob` and inlined — it reads the blob directly via - /// `load_chunk_blob` / `process_blocks_from_blob` rather than through the - /// generic `writeData`'s accessor/processor closures, so it allocates - /// nothing. Handles any alignment and length: fills the partial block, runs - /// whole blocks directly from the blob, then packs the tail (fast for short - /// and long inputs alike). - public func writeBlob(self : Digest, data : Blob) { - assert not self.closed; - let sz = data.size(); - if (sz == 0) return; - let (buf, state) = (self.buffer, self.state); - var pos = 0; - if (buf.i_msg > 0 or not buf.high) { - pos := buf.load_chunk_blob(data, sz, 0); - if (buf.i_msg == 32) { - state.process_block_from_msg(buf.msg); - buf.i_msg := 0; - buf.i_block +%= 1; - }; - }; - // Run whole blocks directly from the blob — but only when block-aligned and - // at least one full block remains, so sub-block inputs skip the call. - if (buf.i_msg == 0 and pos + 64 <= sz) { - let end = state.process_blocks_from_blob(data, pos); - buf.i_block +%= natToNat32(end - pos) / 64; - pos := end; - }; - ignore buf.load_chunk_blob(data, sz, pos); - }; - /// Combine two closed digests in place: replace `self`'s digest with - /// `SHA256(self.digest ++ other.digest)` — a single SHA256 of the two 32-byte - /// digests, leaving the result in `self` (closed). `other` is read-only. The - /// internal-node counterpart of `combineLeaves`. For a double-SHA tree (e.g. - /// Bitcoin) call `fold` after; for a single-SHA tree (each node - /// `SHA256(left ++ right)`, plain concatenation, not RFC 6962) don't. - /// Self-contained — reads both states straight into one block from the IV, - /// then pads; no message buffer, no `Blob`. - /// Traps if `self` or `other` is not closed. - public func combineNodes(self : Digest, other : Digest) { - assert self.closed; - assert other.closed; - let s = self.state; - s.process_merge_block(other.state); // data block (self ++ other) from IV - s.process_padding_block(512); // padding: 64-byte message = 512 bits - }; - /// Hash two 32-byte blobs into `self` as `SHA256(b1 ++ b2)` — a single SHA256 - /// of the 64-byte message, leaving the result in `self` (closed). - /// Self-contained: requires `self` already closed, starts from the IV (no - /// `reset`/`loadIV`), runs the data block plus a hard-coded padding block - /// (512-bit message). The leaf counterpart of `combineNodes`. For a double-SHA - /// tree call `fold` after; for a single-SHA tree don't. - /// Traps if `self` is not closed, or if either blob is not 32 bytes. - public func combineLeaves(self : Digest, b1 : Blob, b2 : Blob) { - assert self.closed; - assert (b1.size() == 32 and b2.size() == 32); - let s = self.state; - s.process_leaf_block(b1, b2); // data block (b1 ++ b2) from IV - s.process_padding_block(512); // padding: 64-byte message = 512 bits - }; - /// Write a `[Nat8]` array to the digest. - /// Traps if `self` is closed. - public func writeArray(self : Digest, data : [Nat8]) { - func process_blocks(pos : Nat) : Nat = self.state.process_blocks_from_array(data, pos); - writeData(self, func(i) = data[i], data.size(), 0, process_blocks); - }; - /// Write a `[var Nat8]` array to the digest. - /// Traps if `self` is closed. - public func writeVarArray(self : Digest, data : [var Nat8]) { - func process_blocks(pos : Nat) : Nat = self.state.process_blocks_from_vararray(data, pos); - writeData(self, func(i) = data[i], data.size(), 0, process_blocks); - }; - /// Write data from a positional accessor function. - /// Traps if `self` is closed. - public func writeAccessor(self : Digest, data : Nat -> Nat8, start : Nat, len : Nat) { - let sz = start + len; - func process_blocks(pos : Nat) : Nat = self.state.process_blocks_from_accessor(data, sz, pos); - writeData(self, data, sz, start, process_blocks); - }; - /// Write data from a reader function. - /// Traps if `self` is closed. - public func writeReader(self : Digest, data : () -> Nat8, len : Nat) { - func process_blocks(pos : Nat) : Nat = self.state.process_blocks_from_reader(data, len, pos); - writeData(self, func(_) = data(), len, 0, process_blocks); - }; - - /// Write data from an iterator to the digest. - /// Traps if `self` is closed. - public func writeIter(self : Digest, data : () -> ?Nat8) { - assert not self.closed; - let (buf, state) = (self.buffer, self.state); - - if (buf.i_msg > 0 or not buf.high) { - buf.load_iter(data); - if (buf.i_msg == 32) { - state.process_block_from_msg(buf.msg); - buf.i_msg := 0; - buf.i_block +%= 1; - }; - }; - - if (buf.i_msg > 0 or not buf.high) return; - - // must have buf.i_msg == 0 and buf.high == true here - // continue to try to read entire blocks at once from the iterator - - state.process(data, buf); - }; - - /// Write SHA256 padding to the digest. - public func writePadding(x : Digest) : () { - let (buf, state) = (x.buffer, x.state); - // Fast path: at a block boundary (empty buffer) the entire padding is a - // single block whose 16 message words are constant except the length, so - // skip the 32-half-word buffer fill and compress the padding block directly. - // Limited to messages whose bit length fits in Nat32 (< 512 MiB, i.e. - // i_block < 2^23); larger messages fall through to the buffer path. - if (buf.i_msg == 0 and buf.high and buf.i_block < 0x80_0000) { - let n_bits : Nat32 = buf.i_block << 9; // i_block * 64 bytes * 8 - state.process_padding_block(n_bits); - return; - }; - let msg = buf.msg; - var i_msg = buf.i_msg; - // n_bits = length of message in bits - let t : Nat8 = if (buf.high) i_msg << 1 else i_msg << 1 +% 1; - let n_bits : Nat64 = ((nat32To64(buf.i_block) << 6) +% intToNat64Wrap(nat8ToNat(t))) << 3; - // separator byte - if (buf.high) { - msg[nat8ToNat(i_msg)] := 0x8000; - } else { - msg[nat8ToNat(i_msg)] := buf.word | 0x80; - }; - i_msg +%= 1; - // zero padding with extra block if necessary - if (i_msg > 28) { - while (i_msg < 32) { - msg[nat8ToNat(i_msg)] := 0; - i_msg +%= 1; - }; - state.process_block_from_msg(msg); // Note: function does not rely on buf.i_msg - i_msg := 0; - // skipping here because we won't use buf.i_block anymore: x.i_block +%= 1; - }; - // zero padding in last block - while (i_msg < 28) { - msg[nat8ToNat(i_msg)] := 0; - i_msg +%= 1; - }; - // 8 length bytes - // Note: this exactly fills the block buffer, hence process_block will get - // triggered by the last writeByte - let (l0, l1, l2, l3, l4, l5, l6, l7) = Prim.explodeNat64(n_bits); - msg[28] := nat8To16(l0) << 8 | nat8To16(l1); - msg[29] := nat8To16(l2) << 8 | nat8To16(l3); - msg[30] := nat8To16(l4) << 8 | nat8To16(l5); - msg[31] := nat8To16(l6) << 8 | nat8To16(l7); - state.process_block_from_msg(msg); // Note: function does not rely on buf.i_msg - // skipping here because we won't use x anymore: buf.i_msg := 0; - }; - - /// Finalize the digest by writing padding. - /// Traps if `self` is closed. - public func close(self : Digest) { - assert not self.closed; - self.closed := true; - writePadding(self); - }; - - /* - public func clone(self : Digest) : Digest { - assert not self.closed; - { - buffer = self.buffer.clone(); - state = self.state.clone(); - var closed = false; - }; - }; - - public func peek(self : Digest) : State { - if (self.closed) return self.state; - let new = clone(self); - close(new); - return new.state; - }; - */ -}; diff --git a/.mops/sha2@0.2.5/src/sha256/state/lib.mo b/.mops/sha2@0.2.5/src/sha256/state/lib.mo deleted file mode 100644 index d492bfa..0000000 --- a/.mops/sha2@0.2.5/src/sha256/state/lib.mo +++ /dev/null @@ -1,73 +0,0 @@ -import Nat "mo:core/Nat"; -import VarArray "mo:core/VarArray"; -import Prim "mo:prim"; - -import fromBlob "process/blocks/blob"; -import fromMerge "process/blocks/merge"; -import fromLeaf "process/blocks/leaf"; -import fromArray "process/blocks/array"; -import fromVarArray "process/blocks/varArray"; -import fromAccessor "process/blocks/accessor"; -import fromReader "process/blocks/reader"; -import fromMsg "process/msg_buffer"; -import fromPadding "process/padding"; -import fromFold "process/fold"; - -module { - /// SHA256 internal state — 8 state words split into 16 `Nat16` half-words (even indices hold the high byte, odd indices the low byte). - // indices 0,2,4,6,8,10,12,14 = high bytes, indices 1,3,5,7,9,11,13,15 = low bytes - public type State = [var Nat16]; - - /// Overwrite the 16 half-words of `self` with the first 16 entries of `vals`. - public func set(self : State, vals : [Nat16]) { - for (i in Nat.range(0, 16)) self[i] := vals[i]; - }; - /// Return an independent copy of the state array. - public let clone = VarArray.clone; - /// Run the SHA256 compression on every full 64-byte block in the input `Blob` (see `process/blocks/blob`). - public let process_blocks_from_blob = fromBlob.process; - /// Inner block of a merge: hash one block of `self`'s digest ++ `sb` from the IV, overwriting `self` (see `process/blocks/merge`). - public let process_merge_block = fromMerge.process; - /// Inner block of a leaf combine: hash one block of `b1 ++ b2` (two 32-byte blobs) from the IV, overwriting `self` (see `process/blocks/leaf`). - public let process_leaf_block = fromLeaf.process; - /// Run the SHA256 compression on every full 64-byte block in the input `[Nat8]` (see `process/blocks/array`). - public let process_blocks_from_array = fromArray.process; - /// Run the SHA256 compression on every full 64-byte block in the input `[var Nat8]` (see `process/blocks/varArray`). - public let process_blocks_from_vararray = fromVarArray.process; - /// Run the SHA256 compression on every full 64-byte block read via a positional accessor (see `process/blocks/accessor`). - public let process_blocks_from_accessor = fromAccessor.process; - /// Run the SHA256 compression on every full 64-byte block read via a reader function (see `process/blocks/reader`). - public let process_blocks_from_reader = fromReader.process; - /// Run the SHA256 compression on a single block already loaded into the message buffer (see `process/msg_buffer`). - public let process_block_from_msg = fromMsg.process; - /// Run the SHA256 compression on the all-constant final padding block for a block-aligned message, encoding only the bit length (see `process/padding`). - public let process_padding_block = fromPadding.process; - /// Hash the 32-byte digest held in the state as a fresh message in one specialized block, overwriting the state with `SHA256(state)` (see `process/fold`). - public let process_fold_block = fromFold.process; - - /// Serialize `self` as a `[Nat8]` of the requested truncation length: `28` for SHA-224 or `32` for SHA-256. - public func toNat8Array(self : State, len : Nat) : [Nat8] { - let (d0, d1) = Prim.explodeNat16(self[0]); - let (d2, d3) = Prim.explodeNat16(self[1]); - let (d4, d5) = Prim.explodeNat16(self[2]); - let (d6, d7) = Prim.explodeNat16(self[3]); - let (d8, d9) = Prim.explodeNat16(self[4]); - let (d10, d11) = Prim.explodeNat16(self[5]); - let (d12, d13) = Prim.explodeNat16(self[6]); - let (d14, d15) = Prim.explodeNat16(self[7]); - let (d16, d17) = Prim.explodeNat16(self[8]); - let (d18, d19) = Prim.explodeNat16(self[9]); - let (d20, d21) = Prim.explodeNat16(self[10]); - let (d22, d23) = Prim.explodeNat16(self[11]); - let (d24, d25) = Prim.explodeNat16(self[12]); - let (d26, d27) = Prim.explodeNat16(self[13]); - - if (len == 28) return [d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19, d20, d21, d22, d23, d24, d25, d26, d27]; - - let (d28, d29) = Prim.explodeNat16(self[14]); - let (d30, d31) = Prim.explodeNat16(self[15]); - - return [d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19, d20, d21, d22, d23, d24, d25, d26, d27, d28, d29, d30, d31]; - }; - -}; diff --git a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/accessor.mo b/.mops/sha2@0.2.5/src/sha256/state/process/blocks/accessor.mo deleted file mode 100644 index d660c0b..0000000 --- a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/accessor.mo +++ /dev/null @@ -1,200 +0,0 @@ -import Prim "mo:prim"; -import K "../constants"; - -module { - let nat32To16 = Prim.nat32ToNat16; - let nat16To32 = Prim.nat16ToNat32; - let nat8To16 = Prim.nat8ToNat16; - - func rot(x : Nat32, y : Nat32) : Nat32 = x <>> y; - - /// Run the SHA256 compression on every full 64-byte block read via `data(i)` for `i` in `[start, sz)`, updating the 16 half-word state `self` in place. Returns the index just past the last block consumed (i.e. `start + 64 * blocks`). - public func process(self : [var Nat16], data : Nat -> Nat8, sz : Nat, start : Nat) : Nat { - var i = start; - // load state registers - var a = nat16To32(self[0]) << 16 | nat16To32(self[1]); - var b = nat16To32(self[2]) << 16 | nat16To32(self[3]); - var c = nat16To32(self[4]) << 16 | nat16To32(self[5]); - var d = nat16To32(self[6]) << 16 | nat16To32(self[7]); - var e = nat16To32(self[8]) << 16 | nat16To32(self[9]); - var f = nat16To32(self[10]) << 16 | nat16To32(self[11]); - var g = nat16To32(self[12]) << 16 | nat16To32(self[13]); - var h = nat16To32(self[14]) << 16 | nat16To32(self[15]); - var t = 0 : Nat32; - var i_max : Nat = i + ((sz - i) / 64) * 64; - while (i < i_max) { - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - let w00 = nat16To32(nat8To16(data(i))) << 24 | nat16To32(nat8To16(data(i + 1))) << 16 | nat16To32(nat8To16(data(i + 2))) << 8 | nat16To32(nat8To16(data(i + 3))); - let w01 = nat16To32(nat8To16(data(i + 4))) << 24 | nat16To32(nat8To16(data(i + 5))) << 16 | nat16To32(nat8To16(data(i + 6))) << 8 | nat16To32(nat8To16(data(i + 7))); - let w02 = nat16To32(nat8To16(data(i + 8))) << 24 | nat16To32(nat8To16(data(i + 9))) << 16 | nat16To32(nat8To16(data(i + 10))) << 8 | nat16To32(nat8To16(data(i + 11))); - let w03 = nat16To32(nat8To16(data(i + 12))) << 24 | nat16To32(nat8To16(data(i + 13))) << 16 | nat16To32(nat8To16(data(i + 14))) << 8 | nat16To32(nat8To16(data(i + 15))); - let w04 = nat16To32(nat8To16(data(i + 16))) << 24 | nat16To32(nat8To16(data(i + 17))) << 16 | nat16To32(nat8To16(data(i + 18))) << 8 | nat16To32(nat8To16(data(i + 19))); - let w05 = nat16To32(nat8To16(data(i + 20))) << 24 | nat16To32(nat8To16(data(i + 21))) << 16 | nat16To32(nat8To16(data(i + 22))) << 8 | nat16To32(nat8To16(data(i + 23))); - let w06 = nat16To32(nat8To16(data(i + 24))) << 24 | nat16To32(nat8To16(data(i + 25))) << 16 | nat16To32(nat8To16(data(i + 26))) << 8 | nat16To32(nat8To16(data(i + 27))); - let w07 = nat16To32(nat8To16(data(i + 28))) << 24 | nat16To32(nat8To16(data(i + 29))) << 16 | nat16To32(nat8To16(data(i + 30))) << 8 | nat16To32(nat8To16(data(i + 31))); - let w08 = nat16To32(nat8To16(data(i + 32))) << 24 | nat16To32(nat8To16(data(i + 33))) << 16 | nat16To32(nat8To16(data(i + 34))) << 8 | nat16To32(nat8To16(data(i + 35))); - let w09 = nat16To32(nat8To16(data(i + 36))) << 24 | nat16To32(nat8To16(data(i + 37))) << 16 | nat16To32(nat8To16(data(i + 38))) << 8 | nat16To32(nat8To16(data(i + 39))); - let w10 = nat16To32(nat8To16(data(i + 40))) << 24 | nat16To32(nat8To16(data(i + 41))) << 16 | nat16To32(nat8To16(data(i + 42))) << 8 | nat16To32(nat8To16(data(i + 43))); - let w11 = nat16To32(nat8To16(data(i + 44))) << 24 | nat16To32(nat8To16(data(i + 45))) << 16 | nat16To32(nat8To16(data(i + 46))) << 8 | nat16To32(nat8To16(data(i + 47))); - let w12 = nat16To32(nat8To16(data(i + 48))) << 24 | nat16To32(nat8To16(data(i + 49))) << 16 | nat16To32(nat8To16(data(i + 50))) << 8 | nat16To32(nat8To16(data(i + 51))); - let w13 = nat16To32(nat8To16(data(i + 52))) << 24 | nat16To32(nat8To16(data(i + 53))) << 16 | nat16To32(nat8To16(data(i + 54))) << 8 | nat16To32(nat8To16(data(i + 55))); - let w14 = nat16To32(nat8To16(data(i + 56))) << 24 | nat16To32(nat8To16(data(i + 57))) << 16 | nat16To32(nat8To16(data(i + 58))) << 8 | nat16To32(nat8To16(data(i + 59))); - let w15 = nat16To32(nat8To16(data(i + 60))) << 24 | nat16To32(nat8To16(data(i + 61))) << 16 | nat16To32(nat8To16(data(i + 62))) << 8 | nat16To32(nat8To16(data(i + 63))); - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - // prettier-ignore - do { - t := h +% K.K00+% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K01+% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K02+% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K03+% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K04+% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K05+% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K06+% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K07+% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K08+% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K09+% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K10+% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K11+% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K12+% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K13+% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K14+% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K15+% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K16+% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K17+% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K18+% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K19+% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K20+% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K21+% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K22+% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K23+% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K24+% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K25+% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K26+% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K27+% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K28+% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K29+% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K30+% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K31+% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K32+% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K33+% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K34+% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K35+% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K36+% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K37+% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K38+% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K39+% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K40+% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K41+% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K42+% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K43+% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K44+% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K45+% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K46+% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K47+% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K48+% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K49+% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K50+% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K51+% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K52+% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K53+% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K54+% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K55+% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K56+% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K57+% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K58+% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K59+% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K60+% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K61+% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K62+% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K63+% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - }; - - // final addition - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - i += 64; - }; - // write state back to registers - self[0] := nat32To16(a >> 16); - self[1] := nat32To16(a & 0xffff); - self[2] := nat32To16(b >> 16); - self[3] := nat32To16(b & 0xffff); - self[4] := nat32To16(c >> 16); - self[5] := nat32To16(c & 0xffff); - self[6] := nat32To16(d >> 16); - self[7] := nat32To16(d & 0xffff); - self[8] := nat32To16(e >> 16); - self[9] := nat32To16(e & 0xffff); - self[10] := nat32To16(f >> 16); - self[11] := nat32To16(f & 0xffff); - self[12] := nat32To16(g >> 16); - self[13] := nat32To16(g & 0xffff); - self[14] := nat32To16(h >> 16); - self[15] := nat32To16(h & 0xffff); - - return i; - }; - -}; diff --git a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/array.mo b/.mops/sha2@0.2.5/src/sha256/state/process/blocks/array.mo deleted file mode 100644 index dff7d40..0000000 --- a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/array.mo +++ /dev/null @@ -1,201 +0,0 @@ -import Prim "mo:prim"; -import K "../constants"; - -module { - let nat32To16 = Prim.nat32ToNat16; - let nat16To32 = Prim.nat16ToNat32; - let nat8To16 = Prim.nat8ToNat16; - - func rot(x : Nat32, y : Nat32) : Nat32 = x <>> y; - - /// Run the SHA256 compression on every full 64-byte block in `data` from index `start` to the end, updating the 16 half-word state `self` in place. Returns the index just past the last block consumed (i.e. `start + 64 * blocks`). - public func process(self : [var Nat16], data : [Nat8], start : Nat) : Nat { - let sz = data.size(); - var i = start; - // load state registers - var a = nat16To32(self[0]) << 16 | nat16To32(self[1]); - var b = nat16To32(self[2]) << 16 | nat16To32(self[3]); - var c = nat16To32(self[4]) << 16 | nat16To32(self[5]); - var d = nat16To32(self[6]) << 16 | nat16To32(self[7]); - var e = nat16To32(self[8]) << 16 | nat16To32(self[9]); - var f = nat16To32(self[10]) << 16 | nat16To32(self[11]); - var g = nat16To32(self[12]) << 16 | nat16To32(self[13]); - var h = nat16To32(self[14]) << 16 | nat16To32(self[15]); - var t = 0 : Nat32; - var i_max : Nat = i + ((sz - i) / 64) * 64; - while (i < i_max) { - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - let w00 = nat16To32(nat8To16(data[i])) << 24 | nat16To32(nat8To16(data[i + 1])) << 16 | nat16To32(nat8To16(data[i + 2])) << 8 | nat16To32(nat8To16(data[i + 3])); - let w01 = nat16To32(nat8To16(data[i + 4])) << 24 | nat16To32(nat8To16(data[i + 5])) << 16 | nat16To32(nat8To16(data[i + 6])) << 8 | nat16To32(nat8To16(data[i + 7])); - let w02 = nat16To32(nat8To16(data[i + 8])) << 24 | nat16To32(nat8To16(data[i + 9])) << 16 | nat16To32(nat8To16(data[i + 10])) << 8 | nat16To32(nat8To16(data[i + 11])); - let w03 = nat16To32(nat8To16(data[i + 12])) << 24 | nat16To32(nat8To16(data[i + 13])) << 16 | nat16To32(nat8To16(data[i + 14])) << 8 | nat16To32(nat8To16(data[i + 15])); - let w04 = nat16To32(nat8To16(data[i + 16])) << 24 | nat16To32(nat8To16(data[i + 17])) << 16 | nat16To32(nat8To16(data[i + 18])) << 8 | nat16To32(nat8To16(data[i + 19])); - let w05 = nat16To32(nat8To16(data[i + 20])) << 24 | nat16To32(nat8To16(data[i + 21])) << 16 | nat16To32(nat8To16(data[i + 22])) << 8 | nat16To32(nat8To16(data[i + 23])); - let w06 = nat16To32(nat8To16(data[i + 24])) << 24 | nat16To32(nat8To16(data[i + 25])) << 16 | nat16To32(nat8To16(data[i + 26])) << 8 | nat16To32(nat8To16(data[i + 27])); - let w07 = nat16To32(nat8To16(data[i + 28])) << 24 | nat16To32(nat8To16(data[i + 29])) << 16 | nat16To32(nat8To16(data[i + 30])) << 8 | nat16To32(nat8To16(data[i + 31])); - let w08 = nat16To32(nat8To16(data[i + 32])) << 24 | nat16To32(nat8To16(data[i + 33])) << 16 | nat16To32(nat8To16(data[i + 34])) << 8 | nat16To32(nat8To16(data[i + 35])); - let w09 = nat16To32(nat8To16(data[i + 36])) << 24 | nat16To32(nat8To16(data[i + 37])) << 16 | nat16To32(nat8To16(data[i + 38])) << 8 | nat16To32(nat8To16(data[i + 39])); - let w10 = nat16To32(nat8To16(data[i + 40])) << 24 | nat16To32(nat8To16(data[i + 41])) << 16 | nat16To32(nat8To16(data[i + 42])) << 8 | nat16To32(nat8To16(data[i + 43])); - let w11 = nat16To32(nat8To16(data[i + 44])) << 24 | nat16To32(nat8To16(data[i + 45])) << 16 | nat16To32(nat8To16(data[i + 46])) << 8 | nat16To32(nat8To16(data[i + 47])); - let w12 = nat16To32(nat8To16(data[i + 48])) << 24 | nat16To32(nat8To16(data[i + 49])) << 16 | nat16To32(nat8To16(data[i + 50])) << 8 | nat16To32(nat8To16(data[i + 51])); - let w13 = nat16To32(nat8To16(data[i + 52])) << 24 | nat16To32(nat8To16(data[i + 53])) << 16 | nat16To32(nat8To16(data[i + 54])) << 8 | nat16To32(nat8To16(data[i + 55])); - let w14 = nat16To32(nat8To16(data[i + 56])) << 24 | nat16To32(nat8To16(data[i + 57])) << 16 | nat16To32(nat8To16(data[i + 58])) << 8 | nat16To32(nat8To16(data[i + 59])); - let w15 = nat16To32(nat8To16(data[i + 60])) << 24 | nat16To32(nat8To16(data[i + 61])) << 16 | nat16To32(nat8To16(data[i + 62])) << 8 | nat16To32(nat8To16(data[i + 63])); - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - // prettier-ignore - do { - t := h +% K.K00+% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K01+% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K02+% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K03+% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K04+% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K05+% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K06+% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K07+% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K08+% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K09+% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K10+% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K11+% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K12+% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K13+% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K14+% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K15+% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K16+% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K17+% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K18+% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K19+% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K20+% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K21+% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K22+% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K23+% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K24+% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K25+% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K26+% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K27+% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K28+% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K29+% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K30+% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K31+% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K32+% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K33+% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K34+% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K35+% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K36+% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K37+% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K38+% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K39+% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K40+% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K41+% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K42+% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K43+% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K44+% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K45+% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K46+% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K47+% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K48+% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K49+% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K50+% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K51+% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K52+% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K53+% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K54+% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K55+% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K56+% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K57+% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K58+% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K59+% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K60+% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K61+% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K62+% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K63+% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - }; - - // final addition - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - i += 64; - }; - // write state back to registers - self[0] := nat32To16(a >> 16); - self[1] := nat32To16(a & 0xffff); - self[2] := nat32To16(b >> 16); - self[3] := nat32To16(b & 0xffff); - self[4] := nat32To16(c >> 16); - self[5] := nat32To16(c & 0xffff); - self[6] := nat32To16(d >> 16); - self[7] := nat32To16(d & 0xffff); - self[8] := nat32To16(e >> 16); - self[9] := nat32To16(e & 0xffff); - self[10] := nat32To16(f >> 16); - self[11] := nat32To16(f & 0xffff); - self[12] := nat32To16(g >> 16); - self[13] := nat32To16(g & 0xffff); - self[14] := nat32To16(h >> 16); - self[15] := nat32To16(h & 0xffff); - - return i; - }; - -}; diff --git a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/blob.mo b/.mops/sha2@0.2.5/src/sha256/state/process/blocks/blob.mo deleted file mode 100644 index d99797a..0000000 --- a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/blob.mo +++ /dev/null @@ -1,201 +0,0 @@ -import Prim "mo:prim"; -import K "../constants"; - -module { - let nat32To16 = Prim.nat32ToNat16; - let nat16To32 = Prim.nat16ToNat32; - let nat8To16 = Prim.nat8ToNat16; - - func rot(x : Nat32, y : Nat32) : Nat32 = x <>> y; - - /// Run the SHA256 compression on every full 64-byte block in `data` from index `start` to the end, updating the 16 half-word state `self` in place. Returns the index just past the last block consumed (i.e. `start + 64 * blocks`). - public func process(self : [var Nat16], data : Blob, start : Nat) : Nat { - let sz = data.size(); - var i = start; - // load state registers - var a = nat16To32(self[0]) << 16 | nat16To32(self[1]); - var b = nat16To32(self[2]) << 16 | nat16To32(self[3]); - var c = nat16To32(self[4]) << 16 | nat16To32(self[5]); - var d = nat16To32(self[6]) << 16 | nat16To32(self[7]); - var e = nat16To32(self[8]) << 16 | nat16To32(self[9]); - var f = nat16To32(self[10]) << 16 | nat16To32(self[11]); - var g = nat16To32(self[12]) << 16 | nat16To32(self[13]); - var h = nat16To32(self[14]) << 16 | nat16To32(self[15]); - var t = 0 : Nat32; - var i_max : Nat = i + ((sz - i) / 64) * 64; - while (i < i_max) { - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - let w00 = nat16To32(nat8To16(data[i])) << 24 | nat16To32(nat8To16(data[i + 1])) << 16 | nat16To32(nat8To16(data[i + 2])) << 8 | nat16To32(nat8To16(data[i + 3])); - let w01 = nat16To32(nat8To16(data[i + 4])) << 24 | nat16To32(nat8To16(data[i + 5])) << 16 | nat16To32(nat8To16(data[i + 6])) << 8 | nat16To32(nat8To16(data[i + 7])); - let w02 = nat16To32(nat8To16(data[i + 8])) << 24 | nat16To32(nat8To16(data[i + 9])) << 16 | nat16To32(nat8To16(data[i + 10])) << 8 | nat16To32(nat8To16(data[i + 11])); - let w03 = nat16To32(nat8To16(data[i + 12])) << 24 | nat16To32(nat8To16(data[i + 13])) << 16 | nat16To32(nat8To16(data[i + 14])) << 8 | nat16To32(nat8To16(data[i + 15])); - let w04 = nat16To32(nat8To16(data[i + 16])) << 24 | nat16To32(nat8To16(data[i + 17])) << 16 | nat16To32(nat8To16(data[i + 18])) << 8 | nat16To32(nat8To16(data[i + 19])); - let w05 = nat16To32(nat8To16(data[i + 20])) << 24 | nat16To32(nat8To16(data[i + 21])) << 16 | nat16To32(nat8To16(data[i + 22])) << 8 | nat16To32(nat8To16(data[i + 23])); - let w06 = nat16To32(nat8To16(data[i + 24])) << 24 | nat16To32(nat8To16(data[i + 25])) << 16 | nat16To32(nat8To16(data[i + 26])) << 8 | nat16To32(nat8To16(data[i + 27])); - let w07 = nat16To32(nat8To16(data[i + 28])) << 24 | nat16To32(nat8To16(data[i + 29])) << 16 | nat16To32(nat8To16(data[i + 30])) << 8 | nat16To32(nat8To16(data[i + 31])); - let w08 = nat16To32(nat8To16(data[i + 32])) << 24 | nat16To32(nat8To16(data[i + 33])) << 16 | nat16To32(nat8To16(data[i + 34])) << 8 | nat16To32(nat8To16(data[i + 35])); - let w09 = nat16To32(nat8To16(data[i + 36])) << 24 | nat16To32(nat8To16(data[i + 37])) << 16 | nat16To32(nat8To16(data[i + 38])) << 8 | nat16To32(nat8To16(data[i + 39])); - let w10 = nat16To32(nat8To16(data[i + 40])) << 24 | nat16To32(nat8To16(data[i + 41])) << 16 | nat16To32(nat8To16(data[i + 42])) << 8 | nat16To32(nat8To16(data[i + 43])); - let w11 = nat16To32(nat8To16(data[i + 44])) << 24 | nat16To32(nat8To16(data[i + 45])) << 16 | nat16To32(nat8To16(data[i + 46])) << 8 | nat16To32(nat8To16(data[i + 47])); - let w12 = nat16To32(nat8To16(data[i + 48])) << 24 | nat16To32(nat8To16(data[i + 49])) << 16 | nat16To32(nat8To16(data[i + 50])) << 8 | nat16To32(nat8To16(data[i + 51])); - let w13 = nat16To32(nat8To16(data[i + 52])) << 24 | nat16To32(nat8To16(data[i + 53])) << 16 | nat16To32(nat8To16(data[i + 54])) << 8 | nat16To32(nat8To16(data[i + 55])); - let w14 = nat16To32(nat8To16(data[i + 56])) << 24 | nat16To32(nat8To16(data[i + 57])) << 16 | nat16To32(nat8To16(data[i + 58])) << 8 | nat16To32(nat8To16(data[i + 59])); - let w15 = nat16To32(nat8To16(data[i + 60])) << 24 | nat16To32(nat8To16(data[i + 61])) << 16 | nat16To32(nat8To16(data[i + 62])) << 8 | nat16To32(nat8To16(data[i + 63])); - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - // prettier-ignore - do { - t := h +% K.K00+% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K01+% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K02+% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K03+% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K04+% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K05+% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K06+% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K07+% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K08+% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K09+% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K10+% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K11+% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K12+% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K13+% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K14+% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K15+% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K16+% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K17+% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K18+% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K19+% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K20+% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K21+% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K22+% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K23+% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K24+% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K25+% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K26+% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K27+% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K28+% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K29+% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K30+% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K31+% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K32+% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K33+% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K34+% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K35+% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K36+% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K37+% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K38+% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K39+% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K40+% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K41+% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K42+% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K43+% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K44+% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K45+% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K46+% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K47+% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K48+% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K49+% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K50+% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K51+% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K52+% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K53+% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K54+% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K55+% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K56+% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K57+% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K58+% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K59+% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K60+% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K61+% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K62+% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K63+% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - }; - - // final addition - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - i += 64; - }; - // write state back to registers - self[0] := nat32To16(a >> 16); - self[1] := nat32To16(a & 0xffff); - self[2] := nat32To16(b >> 16); - self[3] := nat32To16(b & 0xffff); - self[4] := nat32To16(c >> 16); - self[5] := nat32To16(c & 0xffff); - self[6] := nat32To16(d >> 16); - self[7] := nat32To16(d & 0xffff); - self[8] := nat32To16(e >> 16); - self[9] := nat32To16(e & 0xffff); - self[10] := nat32To16(f >> 16); - self[11] := nat32To16(f & 0xffff); - self[12] := nat32To16(g >> 16); - self[13] := nat32To16(g & 0xffff); - self[14] := nat32To16(h >> 16); - self[15] := nat32To16(h & 0xffff); - - return i; - }; - -}; diff --git a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/iter.mo b/.mops/sha2@0.2.5/src/sha256/state/process/blocks/iter.mo deleted file mode 100644 index 1fd3564..0000000 --- a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/iter.mo +++ /dev/null @@ -1,273 +0,0 @@ -import Prim "mo:prim"; -import K "../constants"; -import { type Buffer } "../../../types"; -import _Buffer "../../../buffer"; // buf.load_chunk - -module { - let nat32To16 = Prim.nat32ToNat16; - let nat16To32 = Prim.nat16ToNat32; - let nat8To16 = Prim.nat8ToNat16; - - func rot(x : Nat32, y : Nat32) : Nat32 = x <>> y; - - /// Consume bytes from the iterator `data` in 64-byte chunks, running the SHA256 compression on each full block and updating the 16 half-word state `self` in place. Stops when `data` returns `null`; any trailing partial block is left in `buf` for the digest finalizer to flush. - public func process(self : [var Nat16], data : () -> ?Nat8, buf : Buffer) { - var blocks : Nat32 = 0; - // load state registers - var a = nat16To32(self[0]) << 16 | nat16To32(self[1]); - var b = nat16To32(self[2]) << 16 | nat16To32(self[3]); - var c = nat16To32(self[4]) << 16 | nat16To32(self[5]); - var d = nat16To32(self[6]) << 16 | nat16To32(self[7]); - var e = nat16To32(self[8]) << 16 | nat16To32(self[9]); - var f = nat16To32(self[10]) << 16 | nat16To32(self[11]); - var g = nat16To32(self[12]) << 16 | nat16To32(self[13]); - var h = nat16To32(self[14]) << 16 | nat16To32(self[15]); - var t = 0 : Nat32; - - let backup : [var Nat8] = [var 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - var pos = 0; - ignore do ? { - // prettier-ignore - loop { - let b00 = data()!; backup[0] := b00; pos := 1; - let b01 = data()!; backup[1] := b01; pos := 2; - let b02 = data()!; backup[2] := b02; pos := 3; - let b03 = data()!; backup[3] := b03; pos := 4; - let b04 = data()!; backup[4] := b04; pos := 5; - let b05 = data()!; backup[5] := b05; pos := 6; - let b06 = data()!; backup[6] := b06; pos := 7; - let b07 = data()!; backup[7] := b07; pos := 8; - let b08 = data()!; backup[8] := b08; pos := 9; - let b09 = data()!; backup[9] := b09; pos := 10; - let b10 = data()!; backup[10] := b10; pos := 11; - let b11 = data()!; backup[11] := b11; pos := 12; - let b12 = data()!; backup[12] := b12; pos := 13; - let b13 = data()!; backup[13] := b13; pos := 14; - let b14 = data()!; backup[14] := b14; pos := 15; - let b15 = data()!; backup[15] := b15; pos := 16; - let b16 = data()!; backup[16] := b16; pos := 17; - let b17 = data()!; backup[17] := b17; pos := 18; - let b18 = data()!; backup[18] := b18; pos := 19; - let b19 = data()!; backup[19] := b19; pos := 20; - let b20 = data()!; backup[20] := b20; pos := 21; - let b21 = data()!; backup[21] := b21; pos := 22; - let b22 = data()!; backup[22] := b22; pos := 23; - let b23 = data()!; backup[23] := b23; pos := 24; - let b24 = data()!; backup[24] := b24; pos := 25; - let b25 = data()!; backup[25] := b25; pos := 26; - let b26 = data()!; backup[26] := b26; pos := 27; - let b27 = data()!; backup[27] := b27; pos := 28; - let b28 = data()!; backup[28] := b28; pos := 29; - let b29 = data()!; backup[29] := b29; pos := 30; - let b30 = data()!; backup[30] := b30; pos := 31; - let b31 = data()!; backup[31] := b31; pos := 32; - let b32 = data()!; backup[32] := b32; pos := 33; - let b33 = data()!; backup[33] := b33; pos := 34; - let b34 = data()!; backup[34] := b34; pos := 35; - let b35 = data()!; backup[35] := b35; pos := 36; - let b36 = data()!; backup[36] := b36; pos := 37; - let b37 = data()!; backup[37] := b37; pos := 38; - let b38 = data()!; backup[38] := b38; pos := 39; - let b39 = data()!; backup[39] := b39; pos := 40; - let b40 = data()!; backup[40] := b40; pos := 41; - let b41 = data()!; backup[41] := b41; pos := 42; - let b42 = data()!; backup[42] := b42; pos := 43; - let b43 = data()!; backup[43] := b43; pos := 44; - let b44 = data()!; backup[44] := b44; pos := 45; - let b45 = data()!; backup[45] := b45; pos := 46; - let b46 = data()!; backup[46] := b46; pos := 47; - let b47 = data()!; backup[47] := b47; pos := 48; - let b48 = data()!; backup[48] := b48; pos := 49; - let b49 = data()!; backup[49] := b49; pos := 50; - let b50 = data()!; backup[50] := b50; pos := 51; - let b51 = data()!; backup[51] := b51; pos := 52; - let b52 = data()!; backup[52] := b52; pos := 53; - let b53 = data()!; backup[53] := b53; pos := 54; - let b54 = data()!; backup[54] := b54; pos := 55; - let b55 = data()!; backup[55] := b55; pos := 56; - let b56 = data()!; backup[56] := b56; pos := 57; - let b57 = data()!; backup[57] := b57; pos := 58; - let b58 = data()!; backup[58] := b58; pos := 59; - let b59 = data()!; backup[59] := b59; pos := 60; - let b60 = data()!; backup[60] := b60; pos := 61; - let b61 = data()!; backup[61] := b61; pos := 62; - let b62 = data()!; backup[62] := b62; pos := 63; - let b63 = data()!; backup[63] := b63; pos := 0; - - blocks +%= 1; - - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - let w00 = nat16To32(nat8To16(b00)) << 24 | nat16To32(nat8To16(b01)) << 16 | nat16To32(nat8To16(b02)) << 8 | nat16To32(nat8To16(b03)); - let w01 = nat16To32(nat8To16(b04)) << 24 | nat16To32(nat8To16(b05)) << 16 | nat16To32(nat8To16(b06)) << 8 | nat16To32(nat8To16(b07)); - let w02 = nat16To32(nat8To16(b08)) << 24 | nat16To32(nat8To16(b09)) << 16 | nat16To32(nat8To16(b10)) << 8 | nat16To32(nat8To16(b11)); - let w03 = nat16To32(nat8To16(b12)) << 24 | nat16To32(nat8To16(b13)) << 16 | nat16To32(nat8To16(b14)) << 8 | nat16To32(nat8To16(b15)); - let w04 = nat16To32(nat8To16(b16)) << 24 | nat16To32(nat8To16(b17)) << 16 | nat16To32(nat8To16(b18)) << 8 | nat16To32(nat8To16(b19)); - let w05 = nat16To32(nat8To16(b20)) << 24 | nat16To32(nat8To16(b21)) << 16 | nat16To32(nat8To16(b22)) << 8 | nat16To32(nat8To16(b23)); - let w06 = nat16To32(nat8To16(b24)) << 24 | nat16To32(nat8To16(b25)) << 16 | nat16To32(nat8To16(b26)) << 8 | nat16To32(nat8To16(b27)); - let w07 = nat16To32(nat8To16(b28)) << 24 | nat16To32(nat8To16(b29)) << 16 | nat16To32(nat8To16(b30)) << 8 | nat16To32(nat8To16(b31)); - let w08 = nat16To32(nat8To16(b32)) << 24 | nat16To32(nat8To16(b33)) << 16 | nat16To32(nat8To16(b34)) << 8 | nat16To32(nat8To16(b35)); - let w09 = nat16To32(nat8To16(b36)) << 24 | nat16To32(nat8To16(b37)) << 16 | nat16To32(nat8To16(b38)) << 8 | nat16To32(nat8To16(b39)); - let w10 = nat16To32(nat8To16(b40)) << 24 | nat16To32(nat8To16(b41)) << 16 | nat16To32(nat8To16(b42)) << 8 | nat16To32(nat8To16(b43)); - let w11 = nat16To32(nat8To16(b44)) << 24 | nat16To32(nat8To16(b45)) << 16 | nat16To32(nat8To16(b46)) << 8 | nat16To32(nat8To16(b47)); - let w12 = nat16To32(nat8To16(b48)) << 24 | nat16To32(nat8To16(b49)) << 16 | nat16To32(nat8To16(b50)) << 8 | nat16To32(nat8To16(b51)); - let w13 = nat16To32(nat8To16(b52)) << 24 | nat16To32(nat8To16(b53)) << 16 | nat16To32(nat8To16(b54)) << 8 | nat16To32(nat8To16(b55)); - let w14 = nat16To32(nat8To16(b56)) << 24 | nat16To32(nat8To16(b57)) << 16 | nat16To32(nat8To16(b58)) << 8 | nat16To32(nat8To16(b59)); - let w15 = nat16To32(nat8To16(b60)) << 24 | nat16To32(nat8To16(b61)) << 16 | nat16To32(nat8To16(b62)) << 8 | nat16To32(nat8To16(b63)); - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - t := h +% K.K00+% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K01+% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K02+% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K03+% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K04+% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K05+% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K06+% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K07+% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K08+% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K09+% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K10+% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K11+% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K12+% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K13+% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K14+% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K15+% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K16+% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K17+% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K18+% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K19+% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K20+% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K21+% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K22+% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K23+% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K24+% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K25+% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K26+% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K27+% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K28+% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K29+% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K30+% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K31+% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K32+% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K33+% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K34+% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K35+% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K36+% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K37+% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K38+% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K39+% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K40+% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K41+% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K42+% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K43+% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K44+% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K45+% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K46+% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K47+% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K48+% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K49+% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K50+% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K51+% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K52+% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K53+% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K54+% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K55+% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K56+% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K57+% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K58+% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K59+% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K60+% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K61+% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K62+% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K63+% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - - // final addition - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - }; - }; - // write state back to registers - self[0] := nat32To16(a >> 16); - self[1] := nat32To16(a & 0xffff); - self[2] := nat32To16(b >> 16); - self[3] := nat32To16(b & 0xffff); - self[4] := nat32To16(c >> 16); - self[5] := nat32To16(c & 0xffff); - self[6] := nat32To16(d >> 16); - self[7] := nat32To16(d & 0xffff); - self[8] := nat32To16(e >> 16); - self[9] := nat32To16(e & 0xffff); - self[10] := nat32To16(f >> 16); - self[11] := nat32To16(f & 0xffff); - self[12] := nat32To16(g >> 16); - self[13] := nat32To16(g & 0xffff); - self[14] := nat32To16(h >> 16); - self[15] := nat32To16(h & 0xffff); - - buf.i_block +%= blocks; - - // write remaining bytes from backup to buffer - ignore buf.load_chunk(func(i) = backup[i], pos, 0); - }; - -}; diff --git a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/leaf.mo b/.mops/sha2@0.2.5/src/sha256/state/process/blocks/leaf.mo deleted file mode 100644 index 49fff46..0000000 --- a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/leaf.mo +++ /dev/null @@ -1,199 +0,0 @@ -import Prim "mo:prim"; -import K "../constants"; - -module { - let nat32To16 = Prim.nat32ToNat16; - let nat16To32 = Prim.nat16ToNat32; - let nat8To16 = Prim.nat8ToNat16; - - func rot(x : Nat32, y : Nat32) : Nat32 = x <>> y; - - /// Inner block of a leaf combine: hash one 64-byte block whose words 0..7 are - /// blob `b1` and words 8..15 are blob `b2` (each 32 bytes), starting the - /// compression from the SHA256 IV, and overwrite `self` with the result. - /// Produces the first compression of SHA256(b1 ++ b2); the caller adds the - /// padding block. No message buffer, no allocation. - public func process(self : [var Nat16], b1 : Blob, b2 : Blob) : () { - // message words 0..7 from b1, 8..15 from b2 - let w00 = nat16To32(nat8To16(b1[0])) << 24 | nat16To32(nat8To16(b1[1])) << 16 | nat16To32(nat8To16(b1[2])) << 8 | nat16To32(nat8To16(b1[3])); - let w01 = nat16To32(nat8To16(b1[4])) << 24 | nat16To32(nat8To16(b1[5])) << 16 | nat16To32(nat8To16(b1[6])) << 8 | nat16To32(nat8To16(b1[7])); - let w02 = nat16To32(nat8To16(b1[8])) << 24 | nat16To32(nat8To16(b1[9])) << 16 | nat16To32(nat8To16(b1[10])) << 8 | nat16To32(nat8To16(b1[11])); - let w03 = nat16To32(nat8To16(b1[12])) << 24 | nat16To32(nat8To16(b1[13])) << 16 | nat16To32(nat8To16(b1[14])) << 8 | nat16To32(nat8To16(b1[15])); - let w04 = nat16To32(nat8To16(b1[16])) << 24 | nat16To32(nat8To16(b1[17])) << 16 | nat16To32(nat8To16(b1[18])) << 8 | nat16To32(nat8To16(b1[19])); - let w05 = nat16To32(nat8To16(b1[20])) << 24 | nat16To32(nat8To16(b1[21])) << 16 | nat16To32(nat8To16(b1[22])) << 8 | nat16To32(nat8To16(b1[23])); - let w06 = nat16To32(nat8To16(b1[24])) << 24 | nat16To32(nat8To16(b1[25])) << 16 | nat16To32(nat8To16(b1[26])) << 8 | nat16To32(nat8To16(b1[27])); - let w07 = nat16To32(nat8To16(b1[28])) << 24 | nat16To32(nat8To16(b1[29])) << 16 | nat16To32(nat8To16(b1[30])) << 8 | nat16To32(nat8To16(b1[31])); - let w08 = nat16To32(nat8To16(b2[0])) << 24 | nat16To32(nat8To16(b2[1])) << 16 | nat16To32(nat8To16(b2[2])) << 8 | nat16To32(nat8To16(b2[3])); - let w09 = nat16To32(nat8To16(b2[4])) << 24 | nat16To32(nat8To16(b2[5])) << 16 | nat16To32(nat8To16(b2[6])) << 8 | nat16To32(nat8To16(b2[7])); - let w10 = nat16To32(nat8To16(b2[8])) << 24 | nat16To32(nat8To16(b2[9])) << 16 | nat16To32(nat8To16(b2[10])) << 8 | nat16To32(nat8To16(b2[11])); - let w11 = nat16To32(nat8To16(b2[12])) << 24 | nat16To32(nat8To16(b2[13])) << 16 | nat16To32(nat8To16(b2[14])) << 8 | nat16To32(nat8To16(b2[15])); - let w12 = nat16To32(nat8To16(b2[16])) << 24 | nat16To32(nat8To16(b2[17])) << 16 | nat16To32(nat8To16(b2[18])) << 8 | nat16To32(nat8To16(b2[19])); - let w13 = nat16To32(nat8To16(b2[20])) << 24 | nat16To32(nat8To16(b2[21])) << 16 | nat16To32(nat8To16(b2[22])) << 8 | nat16To32(nat8To16(b2[23])); - let w14 = nat16To32(nat8To16(b2[24])) << 24 | nat16To32(nat8To16(b2[25])) << 16 | nat16To32(nat8To16(b2[26])) << 8 | nat16To32(nat8To16(b2[27])); - let w15 = nat16To32(nat8To16(b2[28])) << 24 | nat16To32(nat8To16(b2[29])) << 16 | nat16To32(nat8To16(b2[30])) << 8 | nat16To32(nat8To16(b2[31])); - - // compression registers start at the SHA256 IV - var a = 0x6a09e667 : Nat32; - var b = 0xbb67ae85 : Nat32; - var c = 0x3c6ef372 : Nat32; - var d = 0xa54ff53a : Nat32; - var e = 0x510e527f : Nat32; - var f = 0x9b05688c : Nat32; - var g = 0x1f83d9ab : Nat32; - var h = 0x5be0cd19 : Nat32; - var t = 0 : Nat32; - - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - - // prettier-ignore - do { - t := h +% K.K00+% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K01+% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K02+% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K03+% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K04+% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K05+% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K06+% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K07+% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K08+% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K09+% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K10+% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K11+% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K12+% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K13+% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K14+% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K15+% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K16+% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K17+% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K18+% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K19+% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K20+% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K21+% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K22+% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K23+% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K24+% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K25+% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K26+% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K27+% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K28+% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K29+% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K30+% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K31+% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K32+% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K33+% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K34+% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K35+% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K36+% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K37+% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K38+% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K39+% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K40+% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K41+% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K42+% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K43+% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K44+% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K45+% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K46+% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K47+% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K48+% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K49+% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K50+% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K51+% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K52+% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K53+% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K54+% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K55+% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K56+% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K57+% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K58+% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K59+% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K60+% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K61+% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K62+% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K63+% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - }; - - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - self[0] := nat32To16(a >> 16); - self[1] := nat32To16(a & 0xffff); - self[2] := nat32To16(b >> 16); - self[3] := nat32To16(b & 0xffff); - self[4] := nat32To16(c >> 16); - self[5] := nat32To16(c & 0xffff); - self[6] := nat32To16(d >> 16); - self[7] := nat32To16(d & 0xffff); - self[8] := nat32To16(e >> 16); - self[9] := nat32To16(e & 0xffff); - self[10] := nat32To16(f >> 16); - self[11] := nat32To16(f & 0xffff); - self[12] := nat32To16(g >> 16); - self[13] := nat32To16(g & 0xffff); - self[14] := nat32To16(h >> 16); - self[15] := nat32To16(h & 0xffff); - }; - -}; diff --git a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/merge.mo b/.mops/sha2@0.2.5/src/sha256/state/process/blocks/merge.mo deleted file mode 100644 index 4c74c5e..0000000 --- a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/merge.mo +++ /dev/null @@ -1,199 +0,0 @@ -import Prim "mo:prim"; -import K "../constants"; - -module { - let nat32To16 = Prim.nat32ToNat16; - let nat16To32 = Prim.nat16ToNat32; - - func rot(x : Nat32, y : Nat32) : Nat32 = x <>> y; - - /// Inner block of a merge: hash one 64-byte block whose words 0..7 are - /// `self`'s own 32-byte digest and words 8..15 are `sb` (another digest), - /// starting the compression from the SHA256 IV, and overwrite `self` with the - /// result. `self`'s words are read into locals first, so writing the result - /// back into `self` is safe. Produces the first compression of - /// SHA256(self.digest ++ sb.digest); the caller adds the padding block. - public func process(self : [var Nat16], sb : [var Nat16]) : () { - // message words 0..7 from self's digest, 8..15 from sb - let w00 = nat16To32(self[0]) << 16 | nat16To32(self[1]); - let w01 = nat16To32(self[2]) << 16 | nat16To32(self[3]); - let w02 = nat16To32(self[4]) << 16 | nat16To32(self[5]); - let w03 = nat16To32(self[6]) << 16 | nat16To32(self[7]); - let w04 = nat16To32(self[8]) << 16 | nat16To32(self[9]); - let w05 = nat16To32(self[10]) << 16 | nat16To32(self[11]); - let w06 = nat16To32(self[12]) << 16 | nat16To32(self[13]); - let w07 = nat16To32(self[14]) << 16 | nat16To32(self[15]); - let w08 = nat16To32(sb[0]) << 16 | nat16To32(sb[1]); - let w09 = nat16To32(sb[2]) << 16 | nat16To32(sb[3]); - let w10 = nat16To32(sb[4]) << 16 | nat16To32(sb[5]); - let w11 = nat16To32(sb[6]) << 16 | nat16To32(sb[7]); - let w12 = nat16To32(sb[8]) << 16 | nat16To32(sb[9]); - let w13 = nat16To32(sb[10]) << 16 | nat16To32(sb[11]); - let w14 = nat16To32(sb[12]) << 16 | nat16To32(sb[13]); - let w15 = nat16To32(sb[14]) << 16 | nat16To32(sb[15]); - - // compression registers start at the SHA256 IV - var a = 0x6a09e667 : Nat32; - var b = 0xbb67ae85 : Nat32; - var c = 0x3c6ef372 : Nat32; - var d = 0xa54ff53a : Nat32; - var e = 0x510e527f : Nat32; - var f = 0x9b05688c : Nat32; - var g = 0x1f83d9ab : Nat32; - var h = 0x5be0cd19 : Nat32; - var t = 0 : Nat32; - - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - - // prettier-ignore - do { - t := h +% K.K00+% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K01+% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K02+% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K03+% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K04+% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K05+% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K06+% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K07+% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K08+% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K09+% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K10+% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K11+% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K12+% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K13+% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K14+% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K15+% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K16+% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K17+% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K18+% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K19+% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K20+% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K21+% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K22+% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K23+% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K24+% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K25+% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K26+% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K27+% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K28+% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K29+% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K30+% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K31+% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K32+% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K33+% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K34+% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K35+% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K36+% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K37+% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K38+% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K39+% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K40+% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K41+% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K42+% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K43+% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K44+% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K45+% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K46+% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K47+% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K48+% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K49+% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K50+% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K51+% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K52+% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K53+% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K54+% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K55+% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K56+% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K57+% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K58+% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K59+% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K60+% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K61+% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K62+% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K63+% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - }; - - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - self[0] := nat32To16(a >> 16); - self[1] := nat32To16(a & 0xffff); - self[2] := nat32To16(b >> 16); - self[3] := nat32To16(b & 0xffff); - self[4] := nat32To16(c >> 16); - self[5] := nat32To16(c & 0xffff); - self[6] := nat32To16(d >> 16); - self[7] := nat32To16(d & 0xffff); - self[8] := nat32To16(e >> 16); - self[9] := nat32To16(e & 0xffff); - self[10] := nat32To16(f >> 16); - self[11] := nat32To16(f & 0xffff); - self[12] := nat32To16(g >> 16); - self[13] := nat32To16(g & 0xffff); - self[14] := nat32To16(h >> 16); - self[15] := nat32To16(h & 0xffff); - }; - -}; diff --git a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/reader.mo b/.mops/sha2@0.2.5/src/sha256/state/process/blocks/reader.mo deleted file mode 100644 index d13bf19..0000000 --- a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/reader.mo +++ /dev/null @@ -1,200 +0,0 @@ -import Prim "mo:prim"; -import K "../constants"; - -module { - let nat32To16 = Prim.nat32ToNat16; - let nat16To32 = Prim.nat16ToNat32; - let nat8To16 = Prim.nat8ToNat16; - - func rot(x : Nat32, y : Nat32) : Nat32 = x <>> y; - - /// Run the SHA256 compression on every full 64-byte block read via repeated calls to `data`, updating the 16 half-word state `self` in place. Treats `start` as the byte-position counter and stops once `start + bytes_consumed` would exceed `sz`. Returns the index just past the last block consumed (i.e. `start + 64 * blocks`). - public func process(self : [var Nat16], data : () -> Nat8, sz : Nat, start : Nat) : Nat { - var i = start; - // load state registers - var a = nat16To32(self[0]) << 16 | nat16To32(self[1]); - var b = nat16To32(self[2]) << 16 | nat16To32(self[3]); - var c = nat16To32(self[4]) << 16 | nat16To32(self[5]); - var d = nat16To32(self[6]) << 16 | nat16To32(self[7]); - var e = nat16To32(self[8]) << 16 | nat16To32(self[9]); - var f = nat16To32(self[10]) << 16 | nat16To32(self[11]); - var g = nat16To32(self[12]) << 16 | nat16To32(self[13]); - var h = nat16To32(self[14]) << 16 | nat16To32(self[15]); - var t = 0 : Nat32; - var i_max : Nat = i + ((sz - i) / 64) * 64; - while (i < i_max) { - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - let w00 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w01 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w02 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w03 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w04 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w05 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w06 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w07 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w08 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w09 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w10 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w11 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w12 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w13 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w14 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w15 = nat16To32(nat8To16(data())) << 24 | nat16To32(nat8To16(data())) << 16 | nat16To32(nat8To16(data())) << 8 | nat16To32(nat8To16(data())); - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - // prettier-ignore - do { - t := h +% K.K00+% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K01+% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K02+% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K03+% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K04+% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K05+% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K06+% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K07+% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K08+% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K09+% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K10+% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K11+% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K12+% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K13+% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K14+% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K15+% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K16+% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K17+% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K18+% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K19+% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K20+% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K21+% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K22+% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K23+% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K24+% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K25+% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K26+% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K27+% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K28+% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K29+% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K30+% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K31+% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K32+% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K33+% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K34+% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K35+% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K36+% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K37+% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K38+% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K39+% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K40+% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K41+% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K42+% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K43+% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K44+% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K45+% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K46+% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K47+% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K48+% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K49+% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K50+% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K51+% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K52+% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K53+% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K54+% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K55+% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K56+% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K57+% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K58+% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K59+% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K60+% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K61+% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K62+% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K63+% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - }; - - // final addition - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - i += 64; - }; - // write state back to registers - self[0] := nat32To16(a >> 16); - self[1] := nat32To16(a & 0xffff); - self[2] := nat32To16(b >> 16); - self[3] := nat32To16(b & 0xffff); - self[4] := nat32To16(c >> 16); - self[5] := nat32To16(c & 0xffff); - self[6] := nat32To16(d >> 16); - self[7] := nat32To16(d & 0xffff); - self[8] := nat32To16(e >> 16); - self[9] := nat32To16(e & 0xffff); - self[10] := nat32To16(f >> 16); - self[11] := nat32To16(f & 0xffff); - self[12] := nat32To16(g >> 16); - self[13] := nat32To16(g & 0xffff); - self[14] := nat32To16(h >> 16); - self[15] := nat32To16(h & 0xffff); - - return i; - }; - -}; diff --git a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/varArray.mo b/.mops/sha2@0.2.5/src/sha256/state/process/blocks/varArray.mo deleted file mode 100644 index fd2c360..0000000 --- a/.mops/sha2@0.2.5/src/sha256/state/process/blocks/varArray.mo +++ /dev/null @@ -1,201 +0,0 @@ -import Prim "mo:prim"; -import K "../constants"; - -module { - let nat32To16 = Prim.nat32ToNat16; - let nat16To32 = Prim.nat16ToNat32; - let nat8To16 = Prim.nat8ToNat16; - - func rot(x : Nat32, y : Nat32) : Nat32 = x <>> y; - - /// Run the SHA256 compression on every full 64-byte block in `data` from index `start` to the end, updating the 16 half-word state `self` in place. Returns the index just past the last block consumed (i.e. `start + 64 * blocks`). - public func process(self : [var Nat16], data : [var Nat8], start : Nat) : Nat { - let sz = data.size(); - var i = start; - // load state registers - var a = nat16To32(self[0]) << 16 | nat16To32(self[1]); - var b = nat16To32(self[2]) << 16 | nat16To32(self[3]); - var c = nat16To32(self[4]) << 16 | nat16To32(self[5]); - var d = nat16To32(self[6]) << 16 | nat16To32(self[7]); - var e = nat16To32(self[8]) << 16 | nat16To32(self[9]); - var f = nat16To32(self[10]) << 16 | nat16To32(self[11]); - var g = nat16To32(self[12]) << 16 | nat16To32(self[13]); - var h = nat16To32(self[14]) << 16 | nat16To32(self[15]); - var t = 0 : Nat32; - var i_max : Nat = i + ((sz - i) / 64) * 64; - while (i < i_max) { - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - let w00 = nat16To32(nat8To16(data[i])) << 24 | nat16To32(nat8To16(data[i + 1])) << 16 | nat16To32(nat8To16(data[i + 2])) << 8 | nat16To32(nat8To16(data[i + 3])); - let w01 = nat16To32(nat8To16(data[i + 4])) << 24 | nat16To32(nat8To16(data[i + 5])) << 16 | nat16To32(nat8To16(data[i + 6])) << 8 | nat16To32(nat8To16(data[i + 7])); - let w02 = nat16To32(nat8To16(data[i + 8])) << 24 | nat16To32(nat8To16(data[i + 9])) << 16 | nat16To32(nat8To16(data[i + 10])) << 8 | nat16To32(nat8To16(data[i + 11])); - let w03 = nat16To32(nat8To16(data[i + 12])) << 24 | nat16To32(nat8To16(data[i + 13])) << 16 | nat16To32(nat8To16(data[i + 14])) << 8 | nat16To32(nat8To16(data[i + 15])); - let w04 = nat16To32(nat8To16(data[i + 16])) << 24 | nat16To32(nat8To16(data[i + 17])) << 16 | nat16To32(nat8To16(data[i + 18])) << 8 | nat16To32(nat8To16(data[i + 19])); - let w05 = nat16To32(nat8To16(data[i + 20])) << 24 | nat16To32(nat8To16(data[i + 21])) << 16 | nat16To32(nat8To16(data[i + 22])) << 8 | nat16To32(nat8To16(data[i + 23])); - let w06 = nat16To32(nat8To16(data[i + 24])) << 24 | nat16To32(nat8To16(data[i + 25])) << 16 | nat16To32(nat8To16(data[i + 26])) << 8 | nat16To32(nat8To16(data[i + 27])); - let w07 = nat16To32(nat8To16(data[i + 28])) << 24 | nat16To32(nat8To16(data[i + 29])) << 16 | nat16To32(nat8To16(data[i + 30])) << 8 | nat16To32(nat8To16(data[i + 31])); - let w08 = nat16To32(nat8To16(data[i + 32])) << 24 | nat16To32(nat8To16(data[i + 33])) << 16 | nat16To32(nat8To16(data[i + 34])) << 8 | nat16To32(nat8To16(data[i + 35])); - let w09 = nat16To32(nat8To16(data[i + 36])) << 24 | nat16To32(nat8To16(data[i + 37])) << 16 | nat16To32(nat8To16(data[i + 38])) << 8 | nat16To32(nat8To16(data[i + 39])); - let w10 = nat16To32(nat8To16(data[i + 40])) << 24 | nat16To32(nat8To16(data[i + 41])) << 16 | nat16To32(nat8To16(data[i + 42])) << 8 | nat16To32(nat8To16(data[i + 43])); - let w11 = nat16To32(nat8To16(data[i + 44])) << 24 | nat16To32(nat8To16(data[i + 45])) << 16 | nat16To32(nat8To16(data[i + 46])) << 8 | nat16To32(nat8To16(data[i + 47])); - let w12 = nat16To32(nat8To16(data[i + 48])) << 24 | nat16To32(nat8To16(data[i + 49])) << 16 | nat16To32(nat8To16(data[i + 50])) << 8 | nat16To32(nat8To16(data[i + 51])); - let w13 = nat16To32(nat8To16(data[i + 52])) << 24 | nat16To32(nat8To16(data[i + 53])) << 16 | nat16To32(nat8To16(data[i + 54])) << 8 | nat16To32(nat8To16(data[i + 55])); - let w14 = nat16To32(nat8To16(data[i + 56])) << 24 | nat16To32(nat8To16(data[i + 57])) << 16 | nat16To32(nat8To16(data[i + 58])) << 8 | nat16To32(nat8To16(data[i + 59])); - let w15 = nat16To32(nat8To16(data[i + 60])) << 24 | nat16To32(nat8To16(data[i + 61])) << 16 | nat16To32(nat8To16(data[i + 62])) << 8 | nat16To32(nat8To16(data[i + 63])); - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - // prettier-ignore - do { - t := h +% K.K00+% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K01+% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K02+% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K03+% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K04+% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K05+% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K06+% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K07+% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K08+% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K09+% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K10+% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K11+% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K12+% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K13+% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K14+% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K15+% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K16+% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K17+% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K18+% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K19+% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K20+% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K21+% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K22+% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K23+% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K24+% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K25+% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K26+% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K27+% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K28+% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K29+% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K30+% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K31+% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K32+% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K33+% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K34+% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K35+% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K36+% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K37+% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K38+% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K39+% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K40+% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K41+% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K42+% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K43+% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K44+% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K45+% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K46+% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K47+% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K48+% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K49+% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K50+% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K51+% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K52+% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K53+% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K54+% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K55+% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K56+% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K57+% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K58+% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K59+% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K60+% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K61+% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K62+% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K63+% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - }; - - // final addition - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - i += 64; - }; - // write state back to registers - self[0] := nat32To16(a >> 16); - self[1] := nat32To16(a & 0xffff); - self[2] := nat32To16(b >> 16); - self[3] := nat32To16(b & 0xffff); - self[4] := nat32To16(c >> 16); - self[5] := nat32To16(c & 0xffff); - self[6] := nat32To16(d >> 16); - self[7] := nat32To16(d & 0xffff); - self[8] := nat32To16(e >> 16); - self[9] := nat32To16(e & 0xffff); - self[10] := nat32To16(f >> 16); - self[11] := nat32To16(f & 0xffff); - self[12] := nat32To16(g >> 16); - self[13] := nat32To16(g & 0xffff); - self[14] := nat32To16(h >> 16); - self[15] := nat32To16(h & 0xffff); - - return i; - }; - -}; diff --git a/.mops/sha2@0.2.5/src/sha256/state/process/constants.mo b/.mops/sha2@0.2.5/src/sha256/state/process/constants.mo deleted file mode 100644 index de13ba8..0000000 --- a/.mops/sha2@0.2.5/src/sha256/state/process/constants.mo +++ /dev/null @@ -1,131 +0,0 @@ -/// SHA256 round constants `K00`..`K63` from FIPS 180-4 §4.2.2, exposed as individual `let`s so the unrolled compression loop can reference them by name. -module { - /// SHA256 round constant K00. - public let K00 : Nat32 = 0x428a2f98; - /// SHA256 round constant K01. - public let K01 : Nat32 = 0x71374491; - /// SHA256 round constant K02. - public let K02 : Nat32 = 0xb5c0fbcf; - /// SHA256 round constant K03. - public let K03 : Nat32 = 0xe9b5dba5; - /// SHA256 round constant K04. - public let K04 : Nat32 = 0x3956c25b; - /// SHA256 round constant K05. - public let K05 : Nat32 = 0x59f111f1; - /// SHA256 round constant K06. - public let K06 : Nat32 = 0x923f82a4; - /// SHA256 round constant K07. - public let K07 : Nat32 = 0xab1c5ed5; - /// SHA256 round constant K08. - public let K08 : Nat32 = 0xd807aa98; - /// SHA256 round constant K09. - public let K09 : Nat32 = 0x12835b01; - /// SHA256 round constant K10. - public let K10 : Nat32 = 0x243185be; - /// SHA256 round constant K11. - public let K11 : Nat32 = 0x550c7dc3; - /// SHA256 round constant K12. - public let K12 : Nat32 = 0x72be5d74; - /// SHA256 round constant K13. - public let K13 : Nat32 = 0x80deb1fe; - /// SHA256 round constant K14. - public let K14 : Nat32 = 0x9bdc06a7; - /// SHA256 round constant K15. - public let K15 : Nat32 = 0xc19bf174; - /// SHA256 round constant K16. - public let K16 : Nat32 = 0xe49b69c1; - /// SHA256 round constant K17. - public let K17 : Nat32 = 0xefbe4786; - /// SHA256 round constant K18. - public let K18 : Nat32 = 0x0fc19dc6; - /// SHA256 round constant K19. - public let K19 : Nat32 = 0x240ca1cc; - /// SHA256 round constant K20. - public let K20 : Nat32 = 0x2de92c6f; - /// SHA256 round constant K21. - public let K21 : Nat32 = 0x4a7484aa; - /// SHA256 round constant K22. - public let K22 : Nat32 = 0x5cb0a9dc; - /// SHA256 round constant K23. - public let K23 : Nat32 = 0x76f988da; - /// SHA256 round constant K24. - public let K24 : Nat32 = 0x983e5152; - /// SHA256 round constant K25. - public let K25 : Nat32 = 0xa831c66d; - /// SHA256 round constant K26. - public let K26 : Nat32 = 0xb00327c8; - /// SHA256 round constant K27. - public let K27 : Nat32 = 0xbf597fc7; - /// SHA256 round constant K28. - public let K28 : Nat32 = 0xc6e00bf3; - /// SHA256 round constant K29. - public let K29 : Nat32 = 0xd5a79147; - /// SHA256 round constant K30. - public let K30 : Nat32 = 0x06ca6351; - /// SHA256 round constant K31. - public let K31 : Nat32 = 0x14292967; - /// SHA256 round constant K32. - public let K32 : Nat32 = 0x27b70a85; - /// SHA256 round constant K33. - public let K33 : Nat32 = 0x2e1b2138; - /// SHA256 round constant K34. - public let K34 : Nat32 = 0x4d2c6dfc; - /// SHA256 round constant K35. - public let K35 : Nat32 = 0x53380d13; - /// SHA256 round constant K36. - public let K36 : Nat32 = 0x650a7354; - /// SHA256 round constant K37. - public let K37 : Nat32 = 0x766a0abb; - /// SHA256 round constant K38. - public let K38 : Nat32 = 0x81c2c92e; - /// SHA256 round constant K39. - public let K39 : Nat32 = 0x92722c85; - /// SHA256 round constant K40. - public let K40 : Nat32 = 0xa2bfe8a1; - /// SHA256 round constant K41. - public let K41 : Nat32 = 0xa81a664b; - /// SHA256 round constant K42. - public let K42 : Nat32 = 0xc24b8b70; - /// SHA256 round constant K43. - public let K43 : Nat32 = 0xc76c51a3; - /// SHA256 round constant K44. - public let K44 : Nat32 = 0xd192e819; - /// SHA256 round constant K45. - public let K45 : Nat32 = 0xd6990624; - /// SHA256 round constant K46. - public let K46 : Nat32 = 0xf40e3585; - /// SHA256 round constant K47. - public let K47 : Nat32 = 0x106aa070; - /// SHA256 round constant K48. - public let K48 : Nat32 = 0x19a4c116; - /// SHA256 round constant K49. - public let K49 : Nat32 = 0x1e376c08; - /// SHA256 round constant K50. - public let K50 : Nat32 = 0x2748774c; - /// SHA256 round constant K51. - public let K51 : Nat32 = 0x34b0bcb5; - /// SHA256 round constant K52. - public let K52 : Nat32 = 0x391c0cb3; - /// SHA256 round constant K53. - public let K53 : Nat32 = 0x4ed8aa4a; - /// SHA256 round constant K54. - public let K54 : Nat32 = 0x5b9cca4f; - /// SHA256 round constant K55. - public let K55 : Nat32 = 0x682e6ff3; - /// SHA256 round constant K56. - public let K56 : Nat32 = 0x748f82ee; - /// SHA256 round constant K57. - public let K57 : Nat32 = 0x78a5636f; - /// SHA256 round constant K58. - public let K58 : Nat32 = 0x84c87814; - /// SHA256 round constant K59. - public let K59 : Nat32 = 0x8cc70208; - /// SHA256 round constant K60. - public let K60 : Nat32 = 0x90befffa; - /// SHA256 round constant K61. - public let K61 : Nat32 = 0xa4506ceb; - /// SHA256 round constant K62. - public let K62 : Nat32 = 0xbef9a3f7; - /// SHA256 round constant K63. - public let K63 : Nat32 = 0xc67178f2; -}; diff --git a/.mops/sha2@0.2.5/src/sha256/state/process/fold.mo b/.mops/sha2@0.2.5/src/sha256/state/process/fold.mo deleted file mode 100644 index 6977c94..0000000 --- a/.mops/sha2@0.2.5/src/sha256/state/process/fold.mo +++ /dev/null @@ -1,194 +0,0 @@ -import Prim "mo:prim"; -import K "constants"; - -module { - let nat32To16 = Prim.nat32ToNat16; - let nat16To32 = Prim.nat16ToNat32; - - func rot(x : Nat32, y : Nat32) : Nat32 = x <>> y; - - /// Hash the 32-byte SHA256 digest currently held in `self` as a fresh message - /// (`self` -> SHA256(self)), in one specialized block. The message is the 8 - /// digest words w00..w07, the 0x80 separator word w08, six zero words - /// w09..w14, and the constant bit length 256 in w15; the compression starts - /// from the SHA256 IV. The zero words drop out of the schedule and rounds. - /// Reads the old digest, then overwrites `self` with the new one in place. - /// SHA256 only (the layout/length are size-specific). - public func process(self : [var Nat16]) : () { - // message words 0..7 = the digest currently in the state - let w00 = nat16To32(self[0]) << 16 | nat16To32(self[1]); - let w01 = nat16To32(self[2]) << 16 | nat16To32(self[3]); - let w02 = nat16To32(self[4]) << 16 | nat16To32(self[5]); - let w03 = nat16To32(self[6]) << 16 | nat16To32(self[7]); - let w04 = nat16To32(self[8]) << 16 | nat16To32(self[9]); - let w05 = nat16To32(self[10]) << 16 | nat16To32(self[11]); - let w06 = nat16To32(self[12]) << 16 | nat16To32(self[13]); - let w07 = nat16To32(self[14]) << 16 | nat16To32(self[15]); - let w08 = 0x8000_0000 : Nat32; // 0x80 separator then zeros - let w15 = 256 : Nat32; // bit length of a 32-byte message - - // compression registers start at the SHA256 IV - var a = 0x6a09e667 : Nat32; - var b = 0xbb67ae85 : Nat32; - var c = 0x3c6ef372 : Nat32; - var d = 0xa54ff53a : Nat32; - var e = 0x510e527f : Nat32; - var f = 0x9b05688c : Nat32; - var g = 0x1f83d9ab : Nat32; - var h = 0x5be0cd19 : Nat32; - var t = 0 : Nat32; - - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - - // prettier-ignore - do { - t := h +% K.K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - }; - - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - self[0] := nat32To16(a >> 16); - self[1] := nat32To16(a & 0xffff); - self[2] := nat32To16(b >> 16); - self[3] := nat32To16(b & 0xffff); - self[4] := nat32To16(c >> 16); - self[5] := nat32To16(c & 0xffff); - self[6] := nat32To16(d >> 16); - self[7] := nat32To16(d & 0xffff); - self[8] := nat32To16(e >> 16); - self[9] := nat32To16(e & 0xffff); - self[10] := nat32To16(f >> 16); - self[11] := nat32To16(f & 0xffff); - self[12] := nat32To16(g >> 16); - self[13] := nat32To16(g & 0xffff); - self[14] := nat32To16(h >> 16); - self[15] := nat32To16(h & 0xffff); - }; - -}; diff --git a/.mops/sha2@0.2.5/src/sha256/state/process/msg_buffer.mo b/.mops/sha2@0.2.5/src/sha256/state/process/msg_buffer.mo deleted file mode 100644 index ee6f363..0000000 --- a/.mops/sha2@0.2.5/src/sha256/state/process/msg_buffer.mo +++ /dev/null @@ -1,216 +0,0 @@ -import Prim "mo:prim"; -import K "constants"; - -module { - let nat32To16 = Prim.nat32ToNat16; - let nat16To32 = Prim.nat16ToNat32; - - func rot(x : Nat32, y : Nat32) : Nat32 = x <>> y; - - /// Run the SHA256 compression on a single 512-bit message block already loaded as 32 `Nat16` half-words in `msg`, updating the 16 half-word state `self` in place. - public func process(self : [var Nat16], msg : [var Nat16]) : () { - let w00 = nat16To32(msg[0]) << 16 | nat16To32(msg[1]); - let w01 = nat16To32(msg[2]) << 16 | nat16To32(msg[3]); - let w02 = nat16To32(msg[4]) << 16 | nat16To32(msg[5]); - let w03 = nat16To32(msg[6]) << 16 | nat16To32(msg[7]); - let w04 = nat16To32(msg[8]) << 16 | nat16To32(msg[9]); - let w05 = nat16To32(msg[10]) << 16 | nat16To32(msg[11]); - let w06 = nat16To32(msg[12]) << 16 | nat16To32(msg[13]); - let w07 = nat16To32(msg[14]) << 16 | nat16To32(msg[15]); - let w08 = nat16To32(msg[16]) << 16 | nat16To32(msg[17]); - let w09 = nat16To32(msg[18]) << 16 | nat16To32(msg[19]); - let w10 = nat16To32(msg[20]) << 16 | nat16To32(msg[21]); - let w11 = nat16To32(msg[22]) << 16 | nat16To32(msg[23]); - let w12 = nat16To32(msg[24]) << 16 | nat16To32(msg[25]); - let w13 = nat16To32(msg[26]) << 16 | nat16To32(msg[27]); - let w14 = nat16To32(msg[28]) << 16 | nat16To32(msg[29]); - let w15 = nat16To32(msg[30]) << 16 | nat16To32(msg[31]); - let w16 = w00 +% rot(w01, 07) ^ rot(w01, 18) ^ (w01 >> 03) +% w09 +% rot(w14, 17) ^ rot(w14, 19) ^ (w14 >> 10); - let w17 = w01 +% rot(w02, 07) ^ rot(w02, 18) ^ (w02 >> 03) +% w10 +% rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = w02 +% rot(w03, 07) ^ rot(w03, 18) ^ (w03 >> 03) +% w11 +% rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = w03 +% rot(w04, 07) ^ rot(w04, 18) ^ (w04 >> 03) +% w12 +% rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = w04 +% rot(w05, 07) ^ rot(w05, 18) ^ (w05 >> 03) +% w13 +% rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = w05 +% rot(w06, 07) ^ rot(w06, 18) ^ (w06 >> 03) +% w14 +% rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w06 +% rot(w07, 07) ^ rot(w07, 18) ^ (w07 >> 03) +% w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w07 +% rot(w08, 07) ^ rot(w08, 18) ^ (w08 >> 03) +% w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w08 +% rot(w09, 07) ^ rot(w09, 18) ^ (w09 >> 03) +% w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w09 +% rot(w10, 07) ^ rot(w10, 18) ^ (w10 >> 03) +% w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w10 +% rot(w11, 07) ^ rot(w11, 18) ^ (w11 >> 03) +% w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w11 +% rot(w12, 07) ^ rot(w12, 18) ^ (w12 >> 03) +% w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w12 +% rot(w13, 07) ^ rot(w13, 18) ^ (w13 >> 03) +% w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w13 +% rot(w14, 07) ^ rot(w14, 18) ^ (w14 >> 03) +% w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = w14 +% rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - /* - for ((i, j, k, l, m) in expansion_rounds.vals()) { - // (j,k,l,m) = (i+1,i+9,i+14,i+16) - let (v0, v1) = (x.msg[j], x.msg[l]); - let s0 = rot(v0, 07) ^ rot(v0, 18) ^ (v0 >> 03); - let s1 = rot(v1, 17) ^ rot(v1, 19) ^ (v1 >> 10); - x.msg[m] := x.msg[i] +% s0 +% x.msg[k] +% s1; - }; -*/ - // compress - let a_0 = nat16To32(self[0]) << 16 | nat16To32(self[1]); - let b_0 = nat16To32(self[2]) << 16 | nat16To32(self[3]); - let c_0 = nat16To32(self[4]) << 16 | nat16To32(self[5]); - let d_0 = nat16To32(self[6]) << 16 | nat16To32(self[7]); - let e_0 = nat16To32(self[8]) << 16 | nat16To32(self[9]); - let f_0 = nat16To32(self[10]) << 16 | nat16To32(self[11]); - let g_0 = nat16To32(self[12]) << 16 | nat16To32(self[13]); - let h_0 = nat16To32(self[14]) << 16 | nat16To32(self[15]); - var a = a_0; - var b = b_0; - var c = c_0; - var d = d_0; - var e = e_0; - var f = f_0; - var g = g_0; - var h = h_0; - var t = 0 : Nat32; - - // prettier-ignore - do { - t := h +% K.K00+% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K01+% w01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K02+% w02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K03+% w03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K04+% w04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K05+% w05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K06+% w06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K07+% w07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K08+% w08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K09+% w09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K10+% w10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K11+% w11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K12+% w12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K13+% w13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K14+% w14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K15+% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K16+% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K17+% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K18+% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K19+% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K20+% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K21+% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K22+% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K23+% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K24+% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K25+% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K26+% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K27+% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K28+% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K29+% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K30+% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K31+% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K32+% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K33+% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K34+% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K35+% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K36+% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K37+% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K38+% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K39+% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K40+% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K41+% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K42+% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K43+% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K44+% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K45+% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K46+% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K47+% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K48+% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K49+% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K50+% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K51+% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K52+% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K53+% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K54+% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K55+% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K56+% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K57+% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K58+% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K59+% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K60+% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K61+% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K62+% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K63+% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - }; - - /* - for (i in compression_rounds.keys()) { - let ch = (e & f) ^ (^ e & g); - let maj = (a & b) ^ (a & c) ^ (b & c); - let sigma0 = rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - let sigma1 = rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); - let t = h +% K[i] +% x.msg[i] +% ch +% sigma1; - h := g; - g := f; - f := e; - e := d +% t; - d := c; - c := b; - b := a; - a := t +% maj +% sigma0; - }; -*/ - // final addition - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - self[0] := nat32To16(a >> 16); - self[1] := nat32To16(a & 0xffff); - self[2] := nat32To16(b >> 16); - self[3] := nat32To16(b & 0xffff); - self[4] := nat32To16(c >> 16); - self[5] := nat32To16(c & 0xffff); - self[6] := nat32To16(d >> 16); - self[7] := nat32To16(d & 0xffff); - self[8] := nat32To16(e >> 16); - self[9] := nat32To16(e & 0xffff); - self[10] := nat32To16(f >> 16); - self[11] := nat32To16(f & 0xffff); - self[12] := nat32To16(g >> 16); - self[13] := nat32To16(g & 0xffff); - self[14] := nat32To16(h >> 16); - self[15] := nat32To16(h & 0xffff); - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha256/state/process/padding.mo b/.mops/sha2@0.2.5/src/sha256/state/process/padding.mo deleted file mode 100644 index 382566a..0000000 --- a/.mops/sha2@0.2.5/src/sha256/state/process/padding.mo +++ /dev/null @@ -1,188 +0,0 @@ -import Prim "mo:prim"; -import K "constants"; - -module { - let nat32To16 = Prim.nat32ToNat16; - let nat16To32 = Prim.nat16ToNat32; - - func rot(x : Nat32, y : Nat32) : Nat32 = x <>> y; - - /// Run the SHA256 compression on the final padding block for a block-aligned - /// message (empty buffer at finalize time) whose bit length fits in 32 bits. - /// The 16 message words are all constant except the length: w00 is the 0x80 - /// separator word, w01..w14 are zero (the zero padding and the all-zero high - /// 32 bits of the length), and w15 is the low 32 bits of the bit length, - /// passed in as `n_bits`. The zero words are not materialized and their - /// contributions are dropped from the schedule and the round additions. - /// Updates the 16 half-word state `self` in place. The caller must fall back - /// to the buffer path when the bit length does not fit in `Nat32`. - public func process(self : [var Nat16], n_bits : Nat32) : () { - // load state registers - var a = nat16To32(self[0]) << 16 | nat16To32(self[1]); - var b = nat16To32(self[2]) << 16 | nat16To32(self[3]); - var c = nat16To32(self[4]) << 16 | nat16To32(self[5]); - var d = nat16To32(self[6]) << 16 | nat16To32(self[7]); - var e = nat16To32(self[8]) << 16 | nat16To32(self[9]); - var f = nat16To32(self[10]) << 16 | nat16To32(self[11]); - var g = nat16To32(self[12]) << 16 | nat16To32(self[13]); - var h = nat16To32(self[14]) << 16 | nat16To32(self[15]); - var t = 0 : Nat32; - - // The only non-zero message words: w00 (0x80 separator) and w15 (length). - let w00 = 0x8000_0000 : Nat32; - let w15 = n_bits; - - let w16 = w00; - let w17 = rot(w15, 17) ^ rot(w15, 19) ^ (w15 >> 10); - let w18 = rot(w16, 17) ^ rot(w16, 19) ^ (w16 >> 10); - let w19 = rot(w17, 17) ^ rot(w17, 19) ^ (w17 >> 10); - let w20 = rot(w18, 17) ^ rot(w18, 19) ^ (w18 >> 10); - let w21 = rot(w19, 17) ^ rot(w19, 19) ^ (w19 >> 10); - let w22 = w15 +% rot(w20, 17) ^ rot(w20, 19) ^ (w20 >> 10); - let w23 = w16 +% rot(w21, 17) ^ rot(w21, 19) ^ (w21 >> 10); - let w24 = w17 +% rot(w22, 17) ^ rot(w22, 19) ^ (w22 >> 10); - let w25 = w18 +% rot(w23, 17) ^ rot(w23, 19) ^ (w23 >> 10); - let w26 = w19 +% rot(w24, 17) ^ rot(w24, 19) ^ (w24 >> 10); - let w27 = w20 +% rot(w25, 17) ^ rot(w25, 19) ^ (w25 >> 10); - let w28 = w21 +% rot(w26, 17) ^ rot(w26, 19) ^ (w26 >> 10); - let w29 = w22 +% rot(w27, 17) ^ rot(w27, 19) ^ (w27 >> 10); - let w30 = rot(w15, 07) ^ rot(w15, 18) ^ (w15 >> 03) +% w23 +% rot(w28, 17) ^ rot(w28, 19) ^ (w28 >> 10); - let w31 = w15 +% rot(w16, 07) ^ rot(w16, 18) ^ (w16 >> 03) +% w24 +% rot(w29, 17) ^ rot(w29, 19) ^ (w29 >> 10); - let w32 = w16 +% rot(w17, 07) ^ rot(w17, 18) ^ (w17 >> 03) +% w25 +% rot(w30, 17) ^ rot(w30, 19) ^ (w30 >> 10); - let w33 = w17 +% rot(w18, 07) ^ rot(w18, 18) ^ (w18 >> 03) +% w26 +% rot(w31, 17) ^ rot(w31, 19) ^ (w31 >> 10); - let w34 = w18 +% rot(w19, 07) ^ rot(w19, 18) ^ (w19 >> 03) +% w27 +% rot(w32, 17) ^ rot(w32, 19) ^ (w32 >> 10); - let w35 = w19 +% rot(w20, 07) ^ rot(w20, 18) ^ (w20 >> 03) +% w28 +% rot(w33, 17) ^ rot(w33, 19) ^ (w33 >> 10); - let w36 = w20 +% rot(w21, 07) ^ rot(w21, 18) ^ (w21 >> 03) +% w29 +% rot(w34, 17) ^ rot(w34, 19) ^ (w34 >> 10); - let w37 = w21 +% rot(w22, 07) ^ rot(w22, 18) ^ (w22 >> 03) +% w30 +% rot(w35, 17) ^ rot(w35, 19) ^ (w35 >> 10); - let w38 = w22 +% rot(w23, 07) ^ rot(w23, 18) ^ (w23 >> 03) +% w31 +% rot(w36, 17) ^ rot(w36, 19) ^ (w36 >> 10); - let w39 = w23 +% rot(w24, 07) ^ rot(w24, 18) ^ (w24 >> 03) +% w32 +% rot(w37, 17) ^ rot(w37, 19) ^ (w37 >> 10); - let w40 = w24 +% rot(w25, 07) ^ rot(w25, 18) ^ (w25 >> 03) +% w33 +% rot(w38, 17) ^ rot(w38, 19) ^ (w38 >> 10); - let w41 = w25 +% rot(w26, 07) ^ rot(w26, 18) ^ (w26 >> 03) +% w34 +% rot(w39, 17) ^ rot(w39, 19) ^ (w39 >> 10); - let w42 = w26 +% rot(w27, 07) ^ rot(w27, 18) ^ (w27 >> 03) +% w35 +% rot(w40, 17) ^ rot(w40, 19) ^ (w40 >> 10); - let w43 = w27 +% rot(w28, 07) ^ rot(w28, 18) ^ (w28 >> 03) +% w36 +% rot(w41, 17) ^ rot(w41, 19) ^ (w41 >> 10); - let w44 = w28 +% rot(w29, 07) ^ rot(w29, 18) ^ (w29 >> 03) +% w37 +% rot(w42, 17) ^ rot(w42, 19) ^ (w42 >> 10); - let w45 = w29 +% rot(w30, 07) ^ rot(w30, 18) ^ (w30 >> 03) +% w38 +% rot(w43, 17) ^ rot(w43, 19) ^ (w43 >> 10); - let w46 = w30 +% rot(w31, 07) ^ rot(w31, 18) ^ (w31 >> 03) +% w39 +% rot(w44, 17) ^ rot(w44, 19) ^ (w44 >> 10); - let w47 = w31 +% rot(w32, 07) ^ rot(w32, 18) ^ (w32 >> 03) +% w40 +% rot(w45, 17) ^ rot(w45, 19) ^ (w45 >> 10); - let w48 = w32 +% rot(w33, 07) ^ rot(w33, 18) ^ (w33 >> 03) +% w41 +% rot(w46, 17) ^ rot(w46, 19) ^ (w46 >> 10); - let w49 = w33 +% rot(w34, 07) ^ rot(w34, 18) ^ (w34 >> 03) +% w42 +% rot(w47, 17) ^ rot(w47, 19) ^ (w47 >> 10); - let w50 = w34 +% rot(w35, 07) ^ rot(w35, 18) ^ (w35 >> 03) +% w43 +% rot(w48, 17) ^ rot(w48, 19) ^ (w48 >> 10); - let w51 = w35 +% rot(w36, 07) ^ rot(w36, 18) ^ (w36 >> 03) +% w44 +% rot(w49, 17) ^ rot(w49, 19) ^ (w49 >> 10); - let w52 = w36 +% rot(w37, 07) ^ rot(w37, 18) ^ (w37 >> 03) +% w45 +% rot(w50, 17) ^ rot(w50, 19) ^ (w50 >> 10); - let w53 = w37 +% rot(w38, 07) ^ rot(w38, 18) ^ (w38 >> 03) +% w46 +% rot(w51, 17) ^ rot(w51, 19) ^ (w51 >> 10); - let w54 = w38 +% rot(w39, 07) ^ rot(w39, 18) ^ (w39 >> 03) +% w47 +% rot(w52, 17) ^ rot(w52, 19) ^ (w52 >> 10); - let w55 = w39 +% rot(w40, 07) ^ rot(w40, 18) ^ (w40 >> 03) +% w48 +% rot(w53, 17) ^ rot(w53, 19) ^ (w53 >> 10); - let w56 = w40 +% rot(w41, 07) ^ rot(w41, 18) ^ (w41 >> 03) +% w49 +% rot(w54, 17) ^ rot(w54, 19) ^ (w54 >> 10); - let w57 = w41 +% rot(w42, 07) ^ rot(w42, 18) ^ (w42 >> 03) +% w50 +% rot(w55, 17) ^ rot(w55, 19) ^ (w55 >> 10); - let w58 = w42 +% rot(w43, 07) ^ rot(w43, 18) ^ (w43 >> 03) +% w51 +% rot(w56, 17) ^ rot(w56, 19) ^ (w56 >> 10); - let w59 = w43 +% rot(w44, 07) ^ rot(w44, 18) ^ (w44 >> 03) +% w52 +% rot(w57, 17) ^ rot(w57, 19) ^ (w57 >> 10); - let w60 = w44 +% rot(w45, 07) ^ rot(w45, 18) ^ (w45 >> 03) +% w53 +% rot(w58, 17) ^ rot(w58, 19) ^ (w58 >> 10); - let w61 = w45 +% rot(w46, 07) ^ rot(w46, 18) ^ (w46 >> 03) +% w54 +% rot(w59, 17) ^ rot(w59, 19) ^ (w59 >> 10); - let w62 = w46 +% rot(w47, 07) ^ rot(w47, 18) ^ (w47 >> 03) +% w55 +% rot(w60, 17) ^ rot(w60, 19) ^ (w60 >> 10); - let w63 = w47 +% rot(w48, 07) ^ rot(w48, 18) ^ (w48 >> 03) +% w56 +% rot(w61, 17) ^ rot(w61, 19) ^ (w61 >> 10); - - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - - // prettier-ignore - do { - t := h +% K.K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K01 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K02 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K03 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K04 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K05 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K06 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K07 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K08 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K09 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K10 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K11 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K12 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K13 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K14 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - t := h +% K.K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 06) ^ rot(e, 11) ^ rot(e, 25); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 02) ^ rot(a, 13) ^ rot(a, 22); - }; - - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - self[0] := nat32To16(a >> 16); - self[1] := nat32To16(a & 0xffff); - self[2] := nat32To16(b >> 16); - self[3] := nat32To16(b & 0xffff); - self[4] := nat32To16(c >> 16); - self[5] := nat32To16(c & 0xffff); - self[6] := nat32To16(d >> 16); - self[7] := nat32To16(d & 0xffff); - self[8] := nat32To16(e >> 16); - self[9] := nat32To16(e & 0xffff); - self[10] := nat32To16(f >> 16); - self[11] := nat32To16(f & 0xffff); - self[12] := nat32To16(g >> 16); - self[13] := nat32To16(g & 0xffff); - self[14] := nat32To16(h >> 16); - self[15] := nat32To16(h & 0xffff); - }; - -}; diff --git a/.mops/sha2@0.2.5/src/sha256/types.mo b/.mops/sha2@0.2.5/src/sha256/types.mo deleted file mode 100644 index 6133134..0000000 --- a/.mops/sha2@0.2.5/src/sha256/types.mo +++ /dev/null @@ -1,30 +0,0 @@ -/// SHA256 internal types. - -module { - /// Message buffer. - /// `msg`: block buffer (16 words of 16 bits). - /// `i_msg`: current word index in `msg`. - /// `i_block`: total number of bits hashed so far. - /// `high`: whether we are in the high byte of the current word. - /// `word`: current word being built. - public type Buffer = { - msg : [var Nat16]; - var i_msg : Nat8; - var i_block : Nat32; - var high : Bool; - var word : Nat16; - }; - - /// SHA256 state (8 words of 32 bits, represented as 16 words of 16 bits). - public type State = [var Nat16]; - - /// Digest type without the algorithm field. - /// `buffer`: message buffer. - /// `state`: current hash state. - /// `closed`: whether the digest has been finalized. - public type Digest = { - buffer : Buffer; - state : State; - var closed : Bool; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/constants.mo b/.mops/sha2@0.2.5/src/sha512/constants.mo deleted file mode 100644 index 4a91094..0000000 --- a/.mops/sha2@0.2.5/src/sha512/constants.mo +++ /dev/null @@ -1,163 +0,0 @@ -/// SHA512 round constants `K00`..`K79` from FIPS 180-4 §4.2.3, exposed as individual `let`s so the unrolled compression loop can reference them by name. -module { - /// SHA512 round constant K00. - public let K00 : Nat64 = 0x428a2f98d728ae22; - /// SHA512 round constant K01. - public let K01 : Nat64 = 0x7137449123ef65cd; - /// SHA512 round constant K02. - public let K02 : Nat64 = 0xb5c0fbcfec4d3b2f; - /// SHA512 round constant K03. - public let K03 : Nat64 = 0xe9b5dba58189dbbc; - /// SHA512 round constant K04. - public let K04 : Nat64 = 0x3956c25bf348b538; - /// SHA512 round constant K05. - public let K05 : Nat64 = 0x59f111f1b605d019; - /// SHA512 round constant K06. - public let K06 : Nat64 = 0x923f82a4af194f9b; - /// SHA512 round constant K07. - public let K07 : Nat64 = 0xab1c5ed5da6d8118; - /// SHA512 round constant K08. - public let K08 : Nat64 = 0xd807aa98a3030242; - /// SHA512 round constant K09. - public let K09 : Nat64 = 0x12835b0145706fbe; - /// SHA512 round constant K10. - public let K10 : Nat64 = 0x243185be4ee4b28c; - /// SHA512 round constant K11. - public let K11 : Nat64 = 0x550c7dc3d5ffb4e2; - /// SHA512 round constant K12. - public let K12 : Nat64 = 0x72be5d74f27b896f; - /// SHA512 round constant K13. - public let K13 : Nat64 = 0x80deb1fe3b1696b1; - /// SHA512 round constant K14. - public let K14 : Nat64 = 0x9bdc06a725c71235; - /// SHA512 round constant K15. - public let K15 : Nat64 = 0xc19bf174cf692694; - /// SHA512 round constant K16. - public let K16 : Nat64 = 0xe49b69c19ef14ad2; - /// SHA512 round constant K17. - public let K17 : Nat64 = 0xefbe4786384f25e3; - /// SHA512 round constant K18. - public let K18 : Nat64 = 0x0fc19dc68b8cd5b5; - /// SHA512 round constant K19. - public let K19 : Nat64 = 0x240ca1cc77ac9c65; - /// SHA512 round constant K20. - public let K20 : Nat64 = 0x2de92c6f592b0275; - /// SHA512 round constant K21. - public let K21 : Nat64 = 0x4a7484aa6ea6e483; - /// SHA512 round constant K22. - public let K22 : Nat64 = 0x5cb0a9dcbd41fbd4; - /// SHA512 round constant K23. - public let K23 : Nat64 = 0x76f988da831153b5; - /// SHA512 round constant K24. - public let K24 : Nat64 = 0x983e5152ee66dfab; - /// SHA512 round constant K25. - public let K25 : Nat64 = 0xa831c66d2db43210; - /// SHA512 round constant K26. - public let K26 : Nat64 = 0xb00327c898fb213f; - /// SHA512 round constant K27. - public let K27 : Nat64 = 0xbf597fc7beef0ee4; - /// SHA512 round constant K28. - public let K28 : Nat64 = 0xc6e00bf33da88fc2; - /// SHA512 round constant K29. - public let K29 : Nat64 = 0xd5a79147930aa725; - /// SHA512 round constant K30. - public let K30 : Nat64 = 0x06ca6351e003826f; - /// SHA512 round constant K31. - public let K31 : Nat64 = 0x142929670a0e6e70; - /// SHA512 round constant K32. - public let K32 : Nat64 = 0x27b70a8546d22ffc; - /// SHA512 round constant K33. - public let K33 : Nat64 = 0x2e1b21385c26c926; - /// SHA512 round constant K34. - public let K34 : Nat64 = 0x4d2c6dfc5ac42aed; - /// SHA512 round constant K35. - public let K35 : Nat64 = 0x53380d139d95b3df; - /// SHA512 round constant K36. - public let K36 : Nat64 = 0x650a73548baf63de; - /// SHA512 round constant K37. - public let K37 : Nat64 = 0x766a0abb3c77b2a8; - /// SHA512 round constant K38. - public let K38 : Nat64 = 0x81c2c92e47edaee6; - /// SHA512 round constant K39. - public let K39 : Nat64 = 0x92722c851482353b; - /// SHA512 round constant K40. - public let K40 : Nat64 = 0xa2bfe8a14cf10364; - /// SHA512 round constant K41. - public let K41 : Nat64 = 0xa81a664bbc423001; - /// SHA512 round constant K42. - public let K42 : Nat64 = 0xc24b8b70d0f89791; - /// SHA512 round constant K43. - public let K43 : Nat64 = 0xc76c51a30654be30; - /// SHA512 round constant K44. - public let K44 : Nat64 = 0xd192e819d6ef5218; - /// SHA512 round constant K45. - public let K45 : Nat64 = 0xd69906245565a910; - /// SHA512 round constant K46. - public let K46 : Nat64 = 0xf40e35855771202a; - /// SHA512 round constant K47. - public let K47 : Nat64 = 0x106aa07032bbd1b8; - /// SHA512 round constant K48. - public let K48 : Nat64 = 0x19a4c116b8d2d0c8; - /// SHA512 round constant K49. - public let K49 : Nat64 = 0x1e376c085141ab53; - /// SHA512 round constant K50. - public let K50 : Nat64 = 0x2748774cdf8eeb99; - /// SHA512 round constant K51. - public let K51 : Nat64 = 0x34b0bcb5e19b48a8; - /// SHA512 round constant K52. - public let K52 : Nat64 = 0x391c0cb3c5c95a63; - /// SHA512 round constant K53. - public let K53 : Nat64 = 0x4ed8aa4ae3418acb; - /// SHA512 round constant K54. - public let K54 : Nat64 = 0x5b9cca4f7763e373; - /// SHA512 round constant K55. - public let K55 : Nat64 = 0x682e6ff3d6b2b8a3; - /// SHA512 round constant K56. - public let K56 : Nat64 = 0x748f82ee5defb2fc; - /// SHA512 round constant K57. - public let K57 : Nat64 = 0x78a5636f43172f60; - /// SHA512 round constant K58. - public let K58 : Nat64 = 0x84c87814a1f0ab72; - /// SHA512 round constant K59. - public let K59 : Nat64 = 0x8cc702081a6439ec; - /// SHA512 round constant K60. - public let K60 : Nat64 = 0x90befffa23631e28; - /// SHA512 round constant K61. - public let K61 : Nat64 = 0xa4506cebde82bde9; - /// SHA512 round constant K62. - public let K62 : Nat64 = 0xbef9a3f7b2c67915; - /// SHA512 round constant K63. - public let K63 : Nat64 = 0xc67178f2e372532b; - /// SHA512 round constant K64. - public let K64 : Nat64 = 0xca273eceea26619c; - /// SHA512 round constant K65. - public let K65 : Nat64 = 0xd186b8c721c0c207; - /// SHA512 round constant K66. - public let K66 : Nat64 = 0xeada7dd6cde0eb1e; - /// SHA512 round constant K67. - public let K67 : Nat64 = 0xf57d4f7fee6ed178; - /// SHA512 round constant K68. - public let K68 : Nat64 = 0x06f067aa72176fba; - /// SHA512 round constant K69. - public let K69 : Nat64 = 0x0a637dc5a2c898a6; - /// SHA512 round constant K70. - public let K70 : Nat64 = 0x113f9804bef90dae; - /// SHA512 round constant K71. - public let K71 : Nat64 = 0x1b710b35131c471b; - /// SHA512 round constant K72. - public let K72 : Nat64 = 0x28db77f523047d84; - /// SHA512 round constant K73. - public let K73 : Nat64 = 0x32caab7b40c72493; - /// SHA512 round constant K74. - public let K74 : Nat64 = 0x3c9ebe0a15c9bebc; - /// SHA512 round constant K75. - public let K75 : Nat64 = 0x431d67c49c100d4c; - /// SHA512 round constant K76. - public let K76 : Nat64 = 0x4cc5d4becb3e42b6; - /// SHA512 round constant K77. - public let K77 : Nat64 = 0x597f299cfc657e2a; - /// SHA512 round constant K78. - public let K78 : Nat64 = 0x5fcb6fab3ad6faec; - /// SHA512 round constant K79. - public let K79 : Nat64 = 0x6c44198c4a475817; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/digest/lib.mo b/.mops/sha2@0.2.5/src/sha512/digest/lib.mo deleted file mode 100644 index 7cfe297..0000000 --- a/.mops/sha2@0.2.5/src/sha512/digest/lib.mo +++ /dev/null @@ -1,112 +0,0 @@ -/// SHA512 digest implementation. - -import Nat8 "mo:core/Nat8"; -import Nat64 "mo:core/Nat64"; - -import Byte "../write/byte"; -import Write "../write"; -import ProcessBlock "../process_block"; -import Padding "../padding"; -import Types "../types"; - -module { - - /// Digest type re-export. - public type Digest = Types.Digest; - - /// Append a single byte, processing a full block if one completes. - public func writeByte(self : Digest, val : Nat8) : () = Byte.writeByte(self, val); - - // We must be at a word boundary, i.e. i_byte must be equal to 8 - public func writeWord(self : Digest, val : Nat64) : () { - assert (self.i_byte == 8); - let msg = self.msg; - var i_msg = self.i_msg; - msg[Nat8.toNat(i_msg)] := val; - i_msg +%= 1; - if (i_msg == 16) { - ProcessBlock.process_block_from_buffer(self.s, msg); - self.i_msg := 0; - self.i_block +%= 1; - } else { - self.i_msg := i_msg; - }; - }; - - /// Write a `Blob` to the digest. - /// Traps if `self` is closed. - public func writeBlob(self : Digest, data : Blob) { - Write.blob(self, data); - }; - - /// Write a `[Nat8]` array to the digest. - /// Traps if `self` is closed. - public func writeArray(self : Digest, data : [Nat8]) { - Write.array(self, data); - }; - /// Write a `[var Nat8]` array to the digest. - /// Traps if `self` is closed. - public func writeVarArray(self : Digest, data : [var Nat8]) { - Write.varArray(self, data); - }; - /// Write data from a positional accessor function. - /// Traps if `self` is closed. - public func writeAccessor(self : Digest, data : Nat -> Nat8, start : Nat, len : Nat) { - Write.accessor(self, data, start, len); - }; - /// Write data from a reader function. - /// Traps if `self` is closed. - public func writeReader(self : Digest, data : () -> Nat8, len : Nat) { - Write.reader(self, data, len); - }; - /// Write data from an iterator to the digest. - /// Traps if `self` is closed. - public func writeIter(self : Digest, data : () -> ?Nat8) { - Write.iter(self, data); - }; - - /// Finalize the digest by writing padding. - /// Traps if `self` is closed. - public func close(self : Digest) { - assert not self.closed; - self.closed := true; - // Fast path: at a block boundary (empty buffer — no buffered words and no - // partial word) the entire padding is a single block whose 16 message words - // are constant except the length, so compress it directly and skip the - // 16-word buffer fill (which would also box every Nat64 written). - if (self.i_msg == 0 and self.i_byte == 8) { - let n_bits : Nat64 = (self.i_block << 7) << 3; // i_block * 128 bytes * 8 - Padding.process(self.s, n_bits); - return; - }; - // calculate padding - // t = bytes in the last incomplete block (0-127) - let t : Nat8 = (self.i_msg << 3) +% 8 -% self.i_byte; - // p = length of padding (1-128) - var p : Nat8 = if (t < 112) (112 -% t) else (240 -% t); - // n_bits = length of message in bits - // Note: This implementation only handles messages < 2^64 bits - let n_bits : Nat64 = ((self.i_block << 7) +% Nat64.fromIntWrap(Nat8.toNat(t))) << 3; - - // write 1-7 padding bytes - Byte.writeByte(self, 0x80); - p -%= 1; - while (p & 0x7 != 0) { - Byte.writeByte(self, 0); - p -%= 1; - }; - // write padding words - p >>= 3; - while (p != 0) { - writeWord(self, 0); - p -%= 1; - }; - - // write length (16 bytes) - // Note: this exactly fills the block buffer, hence process_block will get - // triggered by the last writeByte - writeWord(self, 0); - writeWord(self, n_bits); - }; - -}; diff --git a/.mops/sha2@0.2.5/src/sha512/padding.mo b/.mops/sha2@0.2.5/src/sha512/padding.mo deleted file mode 100644 index a8c1812..0000000 --- a/.mops/sha2@0.2.5/src/sha512/padding.mo +++ /dev/null @@ -1,202 +0,0 @@ -import K "constants"; - -module { - func rot(x : Nat64, y : Nat64) : Nat64 = x <>> y; - - /// Run the SHA512 compression on the final padding block for a block-aligned - /// message (empty buffer at finalize time) whose bit length fits in 64 bits. - /// The 16 message words are all constant except the length: w00 is the 0x80 - /// separator word, w01..w14 are zero (the zero padding and the all-zero high - /// 64 bits of the 128-bit length), and w15 is the low 64 bits of the bit - /// length, passed in as `n_bits`. The schedule recurrence is written out in - /// full; the compiler folds the contributions of the zero words. Updates the - /// 8-word state `self` in place. The caller takes this path only at a block - /// boundary (empty buffer); otherwise it uses the buffer padding path. - public func process(self : [var Nat64], n_bits : Nat64) : () { - // The only non-zero message words: w00 (0x80 separator) and w15 (length). - let w00 = 0x8000_0000_0000_0000 : Nat64; - let w01 = 0 : Nat64; - let w02 = 0 : Nat64; - let w03 = 0 : Nat64; - let w04 = 0 : Nat64; - let w05 = 0 : Nat64; - let w06 = 0 : Nat64; - let w07 = 0 : Nat64; - let w08 = 0 : Nat64; - let w09 = 0 : Nat64; - let w10 = 0 : Nat64; - let w11 = 0 : Nat64; - let w12 = 0 : Nat64; - let w13 = 0 : Nat64; - let w14 = 0 : Nat64; - let w15 = n_bits; - let w16 = w00 +% rot(w01, 01) ^ rot(w01, 08) ^ (w01 >> 07) +% w09 +% rot(w14, 19) ^ rot(w14, 61) ^ (w14 >> 06); - let w17 = w01 +% rot(w02, 01) ^ rot(w02, 08) ^ (w02 >> 07) +% w10 +% rot(w15, 19) ^ rot(w15, 61) ^ (w15 >> 06); - let w18 = w02 +% rot(w03, 01) ^ rot(w03, 08) ^ (w03 >> 07) +% w11 +% rot(w16, 19) ^ rot(w16, 61) ^ (w16 >> 06); - let w19 = w03 +% rot(w04, 01) ^ rot(w04, 08) ^ (w04 >> 07) +% w12 +% rot(w17, 19) ^ rot(w17, 61) ^ (w17 >> 06); - let w20 = w04 +% rot(w05, 01) ^ rot(w05, 08) ^ (w05 >> 07) +% w13 +% rot(w18, 19) ^ rot(w18, 61) ^ (w18 >> 06); - let w21 = w05 +% rot(w06, 01) ^ rot(w06, 08) ^ (w06 >> 07) +% w14 +% rot(w19, 19) ^ rot(w19, 61) ^ (w19 >> 06); - let w22 = w06 +% rot(w07, 01) ^ rot(w07, 08) ^ (w07 >> 07) +% w15 +% rot(w20, 19) ^ rot(w20, 61) ^ (w20 >> 06); - let w23 = w07 +% rot(w08, 01) ^ rot(w08, 08) ^ (w08 >> 07) +% w16 +% rot(w21, 19) ^ rot(w21, 61) ^ (w21 >> 06); - let w24 = w08 +% rot(w09, 01) ^ rot(w09, 08) ^ (w09 >> 07) +% w17 +% rot(w22, 19) ^ rot(w22, 61) ^ (w22 >> 06); - let w25 = w09 +% rot(w10, 01) ^ rot(w10, 08) ^ (w10 >> 07) +% w18 +% rot(w23, 19) ^ rot(w23, 61) ^ (w23 >> 06); - let w26 = w10 +% rot(w11, 01) ^ rot(w11, 08) ^ (w11 >> 07) +% w19 +% rot(w24, 19) ^ rot(w24, 61) ^ (w24 >> 06); - let w27 = w11 +% rot(w12, 01) ^ rot(w12, 08) ^ (w12 >> 07) +% w20 +% rot(w25, 19) ^ rot(w25, 61) ^ (w25 >> 06); - let w28 = w12 +% rot(w13, 01) ^ rot(w13, 08) ^ (w13 >> 07) +% w21 +% rot(w26, 19) ^ rot(w26, 61) ^ (w26 >> 06); - let w29 = w13 +% rot(w14, 01) ^ rot(w14, 08) ^ (w14 >> 07) +% w22 +% rot(w27, 19) ^ rot(w27, 61) ^ (w27 >> 06); - let w30 = w14 +% rot(w15, 01) ^ rot(w15, 08) ^ (w15 >> 07) +% w23 +% rot(w28, 19) ^ rot(w28, 61) ^ (w28 >> 06); - let w31 = w15 +% rot(w16, 01) ^ rot(w16, 08) ^ (w16 >> 07) +% w24 +% rot(w29, 19) ^ rot(w29, 61) ^ (w29 >> 06); - let w32 = w16 +% rot(w17, 01) ^ rot(w17, 08) ^ (w17 >> 07) +% w25 +% rot(w30, 19) ^ rot(w30, 61) ^ (w30 >> 06); - let w33 = w17 +% rot(w18, 01) ^ rot(w18, 08) ^ (w18 >> 07) +% w26 +% rot(w31, 19) ^ rot(w31, 61) ^ (w31 >> 06); - let w34 = w18 +% rot(w19, 01) ^ rot(w19, 08) ^ (w19 >> 07) +% w27 +% rot(w32, 19) ^ rot(w32, 61) ^ (w32 >> 06); - let w35 = w19 +% rot(w20, 01) ^ rot(w20, 08) ^ (w20 >> 07) +% w28 +% rot(w33, 19) ^ rot(w33, 61) ^ (w33 >> 06); - let w36 = w20 +% rot(w21, 01) ^ rot(w21, 08) ^ (w21 >> 07) +% w29 +% rot(w34, 19) ^ rot(w34, 61) ^ (w34 >> 06); - let w37 = w21 +% rot(w22, 01) ^ rot(w22, 08) ^ (w22 >> 07) +% w30 +% rot(w35, 19) ^ rot(w35, 61) ^ (w35 >> 06); - let w38 = w22 +% rot(w23, 01) ^ rot(w23, 08) ^ (w23 >> 07) +% w31 +% rot(w36, 19) ^ rot(w36, 61) ^ (w36 >> 06); - let w39 = w23 +% rot(w24, 01) ^ rot(w24, 08) ^ (w24 >> 07) +% w32 +% rot(w37, 19) ^ rot(w37, 61) ^ (w37 >> 06); - let w40 = w24 +% rot(w25, 01) ^ rot(w25, 08) ^ (w25 >> 07) +% w33 +% rot(w38, 19) ^ rot(w38, 61) ^ (w38 >> 06); - let w41 = w25 +% rot(w26, 01) ^ rot(w26, 08) ^ (w26 >> 07) +% w34 +% rot(w39, 19) ^ rot(w39, 61) ^ (w39 >> 06); - let w42 = w26 +% rot(w27, 01) ^ rot(w27, 08) ^ (w27 >> 07) +% w35 +% rot(w40, 19) ^ rot(w40, 61) ^ (w40 >> 06); - let w43 = w27 +% rot(w28, 01) ^ rot(w28, 08) ^ (w28 >> 07) +% w36 +% rot(w41, 19) ^ rot(w41, 61) ^ (w41 >> 06); - let w44 = w28 +% rot(w29, 01) ^ rot(w29, 08) ^ (w29 >> 07) +% w37 +% rot(w42, 19) ^ rot(w42, 61) ^ (w42 >> 06); - let w45 = w29 +% rot(w30, 01) ^ rot(w30, 08) ^ (w30 >> 07) +% w38 +% rot(w43, 19) ^ rot(w43, 61) ^ (w43 >> 06); - let w46 = w30 +% rot(w31, 01) ^ rot(w31, 08) ^ (w31 >> 07) +% w39 +% rot(w44, 19) ^ rot(w44, 61) ^ (w44 >> 06); - let w47 = w31 +% rot(w32, 01) ^ rot(w32, 08) ^ (w32 >> 07) +% w40 +% rot(w45, 19) ^ rot(w45, 61) ^ (w45 >> 06); - let w48 = w32 +% rot(w33, 01) ^ rot(w33, 08) ^ (w33 >> 07) +% w41 +% rot(w46, 19) ^ rot(w46, 61) ^ (w46 >> 06); - let w49 = w33 +% rot(w34, 01) ^ rot(w34, 08) ^ (w34 >> 07) +% w42 +% rot(w47, 19) ^ rot(w47, 61) ^ (w47 >> 06); - let w50 = w34 +% rot(w35, 01) ^ rot(w35, 08) ^ (w35 >> 07) +% w43 +% rot(w48, 19) ^ rot(w48, 61) ^ (w48 >> 06); - let w51 = w35 +% rot(w36, 01) ^ rot(w36, 08) ^ (w36 >> 07) +% w44 +% rot(w49, 19) ^ rot(w49, 61) ^ (w49 >> 06); - let w52 = w36 +% rot(w37, 01) ^ rot(w37, 08) ^ (w37 >> 07) +% w45 +% rot(w50, 19) ^ rot(w50, 61) ^ (w50 >> 06); - let w53 = w37 +% rot(w38, 01) ^ rot(w38, 08) ^ (w38 >> 07) +% w46 +% rot(w51, 19) ^ rot(w51, 61) ^ (w51 >> 06); - let w54 = w38 +% rot(w39, 01) ^ rot(w39, 08) ^ (w39 >> 07) +% w47 +% rot(w52, 19) ^ rot(w52, 61) ^ (w52 >> 06); - let w55 = w39 +% rot(w40, 01) ^ rot(w40, 08) ^ (w40 >> 07) +% w48 +% rot(w53, 19) ^ rot(w53, 61) ^ (w53 >> 06); - let w56 = w40 +% rot(w41, 01) ^ rot(w41, 08) ^ (w41 >> 07) +% w49 +% rot(w54, 19) ^ rot(w54, 61) ^ (w54 >> 06); - let w57 = w41 +% rot(w42, 01) ^ rot(w42, 08) ^ (w42 >> 07) +% w50 +% rot(w55, 19) ^ rot(w55, 61) ^ (w55 >> 06); - let w58 = w42 +% rot(w43, 01) ^ rot(w43, 08) ^ (w43 >> 07) +% w51 +% rot(w56, 19) ^ rot(w56, 61) ^ (w56 >> 06); - let w59 = w43 +% rot(w44, 01) ^ rot(w44, 08) ^ (w44 >> 07) +% w52 +% rot(w57, 19) ^ rot(w57, 61) ^ (w57 >> 06); - let w60 = w44 +% rot(w45, 01) ^ rot(w45, 08) ^ (w45 >> 07) +% w53 +% rot(w58, 19) ^ rot(w58, 61) ^ (w58 >> 06); - let w61 = w45 +% rot(w46, 01) ^ rot(w46, 08) ^ (w46 >> 07) +% w54 +% rot(w59, 19) ^ rot(w59, 61) ^ (w59 >> 06); - let w62 = w46 +% rot(w47, 01) ^ rot(w47, 08) ^ (w47 >> 07) +% w55 +% rot(w60, 19) ^ rot(w60, 61) ^ (w60 >> 06); - let w63 = w47 +% rot(w48, 01) ^ rot(w48, 08) ^ (w48 >> 07) +% w56 +% rot(w61, 19) ^ rot(w61, 61) ^ (w61 >> 06); - let w64 = w48 +% rot(w49, 01) ^ rot(w49, 08) ^ (w49 >> 07) +% w57 +% rot(w62, 19) ^ rot(w62, 61) ^ (w62 >> 06); - let w65 = w49 +% rot(w50, 01) ^ rot(w50, 08) ^ (w50 >> 07) +% w58 +% rot(w63, 19) ^ rot(w63, 61) ^ (w63 >> 06); - let w66 = w50 +% rot(w51, 01) ^ rot(w51, 08) ^ (w51 >> 07) +% w59 +% rot(w64, 19) ^ rot(w64, 61) ^ (w64 >> 06); - let w67 = w51 +% rot(w52, 01) ^ rot(w52, 08) ^ (w52 >> 07) +% w60 +% rot(w65, 19) ^ rot(w65, 61) ^ (w65 >> 06); - let w68 = w52 +% rot(w53, 01) ^ rot(w53, 08) ^ (w53 >> 07) +% w61 +% rot(w66, 19) ^ rot(w66, 61) ^ (w66 >> 06); - let w69 = w53 +% rot(w54, 01) ^ rot(w54, 08) ^ (w54 >> 07) +% w62 +% rot(w67, 19) ^ rot(w67, 61) ^ (w67 >> 06); - let w70 = w54 +% rot(w55, 01) ^ rot(w55, 08) ^ (w55 >> 07) +% w63 +% rot(w68, 19) ^ rot(w68, 61) ^ (w68 >> 06); - let w71 = w55 +% rot(w56, 01) ^ rot(w56, 08) ^ (w56 >> 07) +% w64 +% rot(w69, 19) ^ rot(w69, 61) ^ (w69 >> 06); - let w72 = w56 +% rot(w57, 01) ^ rot(w57, 08) ^ (w57 >> 07) +% w65 +% rot(w70, 19) ^ rot(w70, 61) ^ (w70 >> 06); - let w73 = w57 +% rot(w58, 01) ^ rot(w58, 08) ^ (w58 >> 07) +% w66 +% rot(w71, 19) ^ rot(w71, 61) ^ (w71 >> 06); - let w74 = w58 +% rot(w59, 01) ^ rot(w59, 08) ^ (w59 >> 07) +% w67 +% rot(w72, 19) ^ rot(w72, 61) ^ (w72 >> 06); - let w75 = w59 +% rot(w60, 01) ^ rot(w60, 08) ^ (w60 >> 07) +% w68 +% rot(w73, 19) ^ rot(w73, 61) ^ (w73 >> 06); - let w76 = w60 +% rot(w61, 01) ^ rot(w61, 08) ^ (w61 >> 07) +% w69 +% rot(w74, 19) ^ rot(w74, 61) ^ (w74 >> 06); - let w77 = w61 +% rot(w62, 01) ^ rot(w62, 08) ^ (w62 >> 07) +% w70 +% rot(w75, 19) ^ rot(w75, 61) ^ (w75 >> 06); - let w78 = w62 +% rot(w63, 01) ^ rot(w63, 08) ^ (w63 >> 07) +% w71 +% rot(w76, 19) ^ rot(w76, 61) ^ (w76 >> 06); - let w79 = w63 +% rot(w64, 01) ^ rot(w64, 08) ^ (w64 >> 07) +% w72 +% rot(w77, 19) ^ rot(w77, 61) ^ (w77 >> 06); - - // compress - var a = self[0]; - var b = self[1]; - var c = self[2]; - var d = self[3]; - var e = self[4]; - var f = self[5]; - var g = self[6]; - var h = self[7]; - var t = 0 : Nat64; - // prettier-ignore - do { - t := h +% K.K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K09 +% w09 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K10 +% w10 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K11 +% w11 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K12 +% w12 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K13 +% w13 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K14 +% w14 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K64 +% w64 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K65 +% w65 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K66 +% w66 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K67 +% w67 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K68 +% w68 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K69 +% w69 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K70 +% w70 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K71 +% w71 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K72 +% w72 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K73 +% w73 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K74 +% w74 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K75 +% w75 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K76 +% w76 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K77 +% w77 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K78 +% w78 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K79 +% w79 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - }; - - // final addition - self[0] +%= a; - self[1] +%= b; - self[2] +%= c; - self[3] +%= d; - self[4] +%= e; - self[5] +%= f; - self[6] +%= g; - self[7] +%= h; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/process_block.mo b/.mops/sha2@0.2.5/src/sha512/process_block.mo deleted file mode 100644 index 202d660..0000000 --- a/.mops/sha2@0.2.5/src/sha512/process_block.mo +++ /dev/null @@ -1,219 +0,0 @@ -import K "constants"; - -module { - func rot(x : Nat64, y : Nat64) : Nat64 = x <>> y; - - /// Run the SHA512 compression on a single 1024-bit message block already loaded as 16 `Nat64` words in `msg`, updating the 8-word state `s` in place. - public func process_block_from_buffer(s : [var Nat64], msg : [var Nat64]) : () { - // Below is an inlined and unrolled version of this code: - // for ((i, j, k, l, m) in expansion_rounds.vals()) { - // // (j,k,l,m) = (i+1,i+9,i+14,i+16) - // let (v0, v1) = (msg[j], msg[l]); - // let s0 = rot(v0, 01) ^ rot(v0, 08) ^ (v0 >> 07); - // let s1 = rot(v1, 19) ^ rot(v1, 61) ^ (v1 >> 06); - // msg[m] := msg[i] +% s0 +% msg[k] +% s1; - // }; - let w00 = msg[0]; - let w01 = msg[1]; - let w02 = msg[2]; - let w03 = msg[3]; - let w04 = msg[4]; - let w05 = msg[5]; - let w06 = msg[6]; - let w07 = msg[7]; - let w08 = msg[8]; - let w09 = msg[9]; - let w10 = msg[10]; - let w11 = msg[11]; - let w12 = msg[12]; - let w13 = msg[13]; - let w14 = msg[14]; - let w15 = msg[15]; - let w16 = w00 +% rot(w01, 01) ^ rot(w01, 08) ^ (w01 >> 07) +% w09 +% rot(w14, 19) ^ rot(w14, 61) ^ (w14 >> 06); - let w17 = w01 +% rot(w02, 01) ^ rot(w02, 08) ^ (w02 >> 07) +% w10 +% rot(w15, 19) ^ rot(w15, 61) ^ (w15 >> 06); - let w18 = w02 +% rot(w03, 01) ^ rot(w03, 08) ^ (w03 >> 07) +% w11 +% rot(w16, 19) ^ rot(w16, 61) ^ (w16 >> 06); - let w19 = w03 +% rot(w04, 01) ^ rot(w04, 08) ^ (w04 >> 07) +% w12 +% rot(w17, 19) ^ rot(w17, 61) ^ (w17 >> 06); - let w20 = w04 +% rot(w05, 01) ^ rot(w05, 08) ^ (w05 >> 07) +% w13 +% rot(w18, 19) ^ rot(w18, 61) ^ (w18 >> 06); - let w21 = w05 +% rot(w06, 01) ^ rot(w06, 08) ^ (w06 >> 07) +% w14 +% rot(w19, 19) ^ rot(w19, 61) ^ (w19 >> 06); - let w22 = w06 +% rot(w07, 01) ^ rot(w07, 08) ^ (w07 >> 07) +% w15 +% rot(w20, 19) ^ rot(w20, 61) ^ (w20 >> 06); - let w23 = w07 +% rot(w08, 01) ^ rot(w08, 08) ^ (w08 >> 07) +% w16 +% rot(w21, 19) ^ rot(w21, 61) ^ (w21 >> 06); - let w24 = w08 +% rot(w09, 01) ^ rot(w09, 08) ^ (w09 >> 07) +% w17 +% rot(w22, 19) ^ rot(w22, 61) ^ (w22 >> 06); - let w25 = w09 +% rot(w10, 01) ^ rot(w10, 08) ^ (w10 >> 07) +% w18 +% rot(w23, 19) ^ rot(w23, 61) ^ (w23 >> 06); - let w26 = w10 +% rot(w11, 01) ^ rot(w11, 08) ^ (w11 >> 07) +% w19 +% rot(w24, 19) ^ rot(w24, 61) ^ (w24 >> 06); - let w27 = w11 +% rot(w12, 01) ^ rot(w12, 08) ^ (w12 >> 07) +% w20 +% rot(w25, 19) ^ rot(w25, 61) ^ (w25 >> 06); - let w28 = w12 +% rot(w13, 01) ^ rot(w13, 08) ^ (w13 >> 07) +% w21 +% rot(w26, 19) ^ rot(w26, 61) ^ (w26 >> 06); - let w29 = w13 +% rot(w14, 01) ^ rot(w14, 08) ^ (w14 >> 07) +% w22 +% rot(w27, 19) ^ rot(w27, 61) ^ (w27 >> 06); - let w30 = w14 +% rot(w15, 01) ^ rot(w15, 08) ^ (w15 >> 07) +% w23 +% rot(w28, 19) ^ rot(w28, 61) ^ (w28 >> 06); - let w31 = w15 +% rot(w16, 01) ^ rot(w16, 08) ^ (w16 >> 07) +% w24 +% rot(w29, 19) ^ rot(w29, 61) ^ (w29 >> 06); - let w32 = w16 +% rot(w17, 01) ^ rot(w17, 08) ^ (w17 >> 07) +% w25 +% rot(w30, 19) ^ rot(w30, 61) ^ (w30 >> 06); - let w33 = w17 +% rot(w18, 01) ^ rot(w18, 08) ^ (w18 >> 07) +% w26 +% rot(w31, 19) ^ rot(w31, 61) ^ (w31 >> 06); - let w34 = w18 +% rot(w19, 01) ^ rot(w19, 08) ^ (w19 >> 07) +% w27 +% rot(w32, 19) ^ rot(w32, 61) ^ (w32 >> 06); - let w35 = w19 +% rot(w20, 01) ^ rot(w20, 08) ^ (w20 >> 07) +% w28 +% rot(w33, 19) ^ rot(w33, 61) ^ (w33 >> 06); - let w36 = w20 +% rot(w21, 01) ^ rot(w21, 08) ^ (w21 >> 07) +% w29 +% rot(w34, 19) ^ rot(w34, 61) ^ (w34 >> 06); - let w37 = w21 +% rot(w22, 01) ^ rot(w22, 08) ^ (w22 >> 07) +% w30 +% rot(w35, 19) ^ rot(w35, 61) ^ (w35 >> 06); - let w38 = w22 +% rot(w23, 01) ^ rot(w23, 08) ^ (w23 >> 07) +% w31 +% rot(w36, 19) ^ rot(w36, 61) ^ (w36 >> 06); - let w39 = w23 +% rot(w24, 01) ^ rot(w24, 08) ^ (w24 >> 07) +% w32 +% rot(w37, 19) ^ rot(w37, 61) ^ (w37 >> 06); - let w40 = w24 +% rot(w25, 01) ^ rot(w25, 08) ^ (w25 >> 07) +% w33 +% rot(w38, 19) ^ rot(w38, 61) ^ (w38 >> 06); - let w41 = w25 +% rot(w26, 01) ^ rot(w26, 08) ^ (w26 >> 07) +% w34 +% rot(w39, 19) ^ rot(w39, 61) ^ (w39 >> 06); - let w42 = w26 +% rot(w27, 01) ^ rot(w27, 08) ^ (w27 >> 07) +% w35 +% rot(w40, 19) ^ rot(w40, 61) ^ (w40 >> 06); - let w43 = w27 +% rot(w28, 01) ^ rot(w28, 08) ^ (w28 >> 07) +% w36 +% rot(w41, 19) ^ rot(w41, 61) ^ (w41 >> 06); - let w44 = w28 +% rot(w29, 01) ^ rot(w29, 08) ^ (w29 >> 07) +% w37 +% rot(w42, 19) ^ rot(w42, 61) ^ (w42 >> 06); - let w45 = w29 +% rot(w30, 01) ^ rot(w30, 08) ^ (w30 >> 07) +% w38 +% rot(w43, 19) ^ rot(w43, 61) ^ (w43 >> 06); - let w46 = w30 +% rot(w31, 01) ^ rot(w31, 08) ^ (w31 >> 07) +% w39 +% rot(w44, 19) ^ rot(w44, 61) ^ (w44 >> 06); - let w47 = w31 +% rot(w32, 01) ^ rot(w32, 08) ^ (w32 >> 07) +% w40 +% rot(w45, 19) ^ rot(w45, 61) ^ (w45 >> 06); - let w48 = w32 +% rot(w33, 01) ^ rot(w33, 08) ^ (w33 >> 07) +% w41 +% rot(w46, 19) ^ rot(w46, 61) ^ (w46 >> 06); - let w49 = w33 +% rot(w34, 01) ^ rot(w34, 08) ^ (w34 >> 07) +% w42 +% rot(w47, 19) ^ rot(w47, 61) ^ (w47 >> 06); - let w50 = w34 +% rot(w35, 01) ^ rot(w35, 08) ^ (w35 >> 07) +% w43 +% rot(w48, 19) ^ rot(w48, 61) ^ (w48 >> 06); - let w51 = w35 +% rot(w36, 01) ^ rot(w36, 08) ^ (w36 >> 07) +% w44 +% rot(w49, 19) ^ rot(w49, 61) ^ (w49 >> 06); - let w52 = w36 +% rot(w37, 01) ^ rot(w37, 08) ^ (w37 >> 07) +% w45 +% rot(w50, 19) ^ rot(w50, 61) ^ (w50 >> 06); - let w53 = w37 +% rot(w38, 01) ^ rot(w38, 08) ^ (w38 >> 07) +% w46 +% rot(w51, 19) ^ rot(w51, 61) ^ (w51 >> 06); - let w54 = w38 +% rot(w39, 01) ^ rot(w39, 08) ^ (w39 >> 07) +% w47 +% rot(w52, 19) ^ rot(w52, 61) ^ (w52 >> 06); - let w55 = w39 +% rot(w40, 01) ^ rot(w40, 08) ^ (w40 >> 07) +% w48 +% rot(w53, 19) ^ rot(w53, 61) ^ (w53 >> 06); - let w56 = w40 +% rot(w41, 01) ^ rot(w41, 08) ^ (w41 >> 07) +% w49 +% rot(w54, 19) ^ rot(w54, 61) ^ (w54 >> 06); - let w57 = w41 +% rot(w42, 01) ^ rot(w42, 08) ^ (w42 >> 07) +% w50 +% rot(w55, 19) ^ rot(w55, 61) ^ (w55 >> 06); - let w58 = w42 +% rot(w43, 01) ^ rot(w43, 08) ^ (w43 >> 07) +% w51 +% rot(w56, 19) ^ rot(w56, 61) ^ (w56 >> 06); - let w59 = w43 +% rot(w44, 01) ^ rot(w44, 08) ^ (w44 >> 07) +% w52 +% rot(w57, 19) ^ rot(w57, 61) ^ (w57 >> 06); - let w60 = w44 +% rot(w45, 01) ^ rot(w45, 08) ^ (w45 >> 07) +% w53 +% rot(w58, 19) ^ rot(w58, 61) ^ (w58 >> 06); - let w61 = w45 +% rot(w46, 01) ^ rot(w46, 08) ^ (w46 >> 07) +% w54 +% rot(w59, 19) ^ rot(w59, 61) ^ (w59 >> 06); - let w62 = w46 +% rot(w47, 01) ^ rot(w47, 08) ^ (w47 >> 07) +% w55 +% rot(w60, 19) ^ rot(w60, 61) ^ (w60 >> 06); - let w63 = w47 +% rot(w48, 01) ^ rot(w48, 08) ^ (w48 >> 07) +% w56 +% rot(w61, 19) ^ rot(w61, 61) ^ (w61 >> 06); - let w64 = w48 +% rot(w49, 01) ^ rot(w49, 08) ^ (w49 >> 07) +% w57 +% rot(w62, 19) ^ rot(w62, 61) ^ (w62 >> 06); - let w65 = w49 +% rot(w50, 01) ^ rot(w50, 08) ^ (w50 >> 07) +% w58 +% rot(w63, 19) ^ rot(w63, 61) ^ (w63 >> 06); - let w66 = w50 +% rot(w51, 01) ^ rot(w51, 08) ^ (w51 >> 07) +% w59 +% rot(w64, 19) ^ rot(w64, 61) ^ (w64 >> 06); - let w67 = w51 +% rot(w52, 01) ^ rot(w52, 08) ^ (w52 >> 07) +% w60 +% rot(w65, 19) ^ rot(w65, 61) ^ (w65 >> 06); - let w68 = w52 +% rot(w53, 01) ^ rot(w53, 08) ^ (w53 >> 07) +% w61 +% rot(w66, 19) ^ rot(w66, 61) ^ (w66 >> 06); - let w69 = w53 +% rot(w54, 01) ^ rot(w54, 08) ^ (w54 >> 07) +% w62 +% rot(w67, 19) ^ rot(w67, 61) ^ (w67 >> 06); - let w70 = w54 +% rot(w55, 01) ^ rot(w55, 08) ^ (w55 >> 07) +% w63 +% rot(w68, 19) ^ rot(w68, 61) ^ (w68 >> 06); - let w71 = w55 +% rot(w56, 01) ^ rot(w56, 08) ^ (w56 >> 07) +% w64 +% rot(w69, 19) ^ rot(w69, 61) ^ (w69 >> 06); - let w72 = w56 +% rot(w57, 01) ^ rot(w57, 08) ^ (w57 >> 07) +% w65 +% rot(w70, 19) ^ rot(w70, 61) ^ (w70 >> 06); - let w73 = w57 +% rot(w58, 01) ^ rot(w58, 08) ^ (w58 >> 07) +% w66 +% rot(w71, 19) ^ rot(w71, 61) ^ (w71 >> 06); - let w74 = w58 +% rot(w59, 01) ^ rot(w59, 08) ^ (w59 >> 07) +% w67 +% rot(w72, 19) ^ rot(w72, 61) ^ (w72 >> 06); - let w75 = w59 +% rot(w60, 01) ^ rot(w60, 08) ^ (w60 >> 07) +% w68 +% rot(w73, 19) ^ rot(w73, 61) ^ (w73 >> 06); - let w76 = w60 +% rot(w61, 01) ^ rot(w61, 08) ^ (w61 >> 07) +% w69 +% rot(w74, 19) ^ rot(w74, 61) ^ (w74 >> 06); - let w77 = w61 +% rot(w62, 01) ^ rot(w62, 08) ^ (w62 >> 07) +% w70 +% rot(w75, 19) ^ rot(w75, 61) ^ (w75 >> 06); - let w78 = w62 +% rot(w63, 01) ^ rot(w63, 08) ^ (w63 >> 07) +% w71 +% rot(w76, 19) ^ rot(w76, 61) ^ (w76 >> 06); - let w79 = w63 +% rot(w64, 01) ^ rot(w64, 08) ^ (w64 >> 07) +% w72 +% rot(w77, 19) ^ rot(w77, 61) ^ (w77 >> 06); - - // compress - var a = s[0]; - var b = s[1]; - var c = s[2]; - var d = s[3]; - var e = s[4]; - var f = s[5]; - var g = s[6]; - var h = s[7]; - - // Below is an inlined and unrolled version of this code: - // for (i in compression_rounds.keys()) { - // let ch = (e & f) ^ (^ e & g); - // let maj = (a & b) ^ (a & c) ^ (b & c); - // let sigma0 = rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - // let sigma1 = rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); - // let t = h +% K[i] +% w[i] +% ch +% sigma1; - // h := g; - // g := f; - // f := e; - // e := d +% t; - // d := c; - // c := b; - // b := a; - // a := t +% maj +% sigma0; - // }; - - var t = 0 : Nat64; - // prettier-ignore - do { - t := h +% K.K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K09 +% w09 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K10 +% w10 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K11 +% w11 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K12 +% w12 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K13 +% w13 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K14 +% w14 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K64 +% w64 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K65 +% w65 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K66 +% w66 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K67 +% w67 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K68 +% w68 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K69 +% w69 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K70 +% w70 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K71 +% w71 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K72 +% w72 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K73 +% w73 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K74 +% w74 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K75 +% w75 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K76 +% w76 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K77 +% w77 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K78 +% w78 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K79 +% w79 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - }; - - // final addition - s[0] +%= a; - s[1] +%= b; - s[2] +%= c; - s[3] +%= d; - s[4] +%= e; - s[5] +%= f; - s[6] +%= g; - s[7] +%= h; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/types.mo b/.mops/sha2@0.2.5/src/sha512/types.mo deleted file mode 100644 index a9a9ab7..0000000 --- a/.mops/sha2@0.2.5/src/sha512/types.mo +++ /dev/null @@ -1,23 +0,0 @@ -/// SHA512 internal types. - -module { - /// Digest type. - /// `msg`: message block buffer (16 words of 64 bits). - /// `word`: current word being built. - /// `i_msg`: current word index in `msg`. - /// `i_byte`: current byte index in `word`. - /// `i_block`: total number of bits hashed so far. - /// `s`: current hash state (8 words of 64 bits). - /// `closed`: whether the digest has been finalized. - public type Digest = { - // msg buffer - msg : [var Nat64]; - var word : Nat64; - var i_msg : Nat8; - var i_byte : Nat8; - var i_block : Nat64; - // state variables - s : [var Nat64]; - var closed : Bool; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/whole_blocks/accessor.mo b/.mops/sha2@0.2.5/src/sha512/whole_blocks/accessor.mo deleted file mode 100644 index cd20cbc..0000000 --- a/.mops/sha2@0.2.5/src/sha512/whole_blocks/accessor.mo +++ /dev/null @@ -1,226 +0,0 @@ -import Prim "mo:prim"; -import K "../constants"; - -module { - func rot(x : Nat64, y : Nat64) : Nat64 = x <>> y; - - let nat32To64 = Prim.nat32ToNat64; - let nat16To32 = Prim.nat16ToNat32; - let nat8To16 = Prim.nat8ToNat16; - - /// Run the SHA512 compression on every full 128-byte block read via `data(i)` for `i` in `[start, sz)`. Returns the index just past the last block consumed (i.e. `start + 128 * blocks`). - public func process_blocks(state : [var Nat64], data : Nat -> Nat8, sz : Nat, start : Nat) : Nat { - var i = start; - // load state registers - var a = state[0]; - var b = state[1]; - var c = state[2]; - var d = state[3]; - var e = state[4]; - var f = state[5]; - var g = state[6]; - var h = state[7]; - var t = 0 : Nat64; - var i_max : Nat = i + ((sz - i) / 128) * 128; - while (i < i_max) { - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - - let w00 = nat32To64(nat16To32(nat8To16(data(i + 0)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 1)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 2)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 3)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 4)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 5)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 6)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 7)))); - let w01 = nat32To64(nat16To32(nat8To16(data(i + 8)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 9)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 10)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 11)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 12)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 13)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 14)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 15)))); - let w02 = nat32To64(nat16To32(nat8To16(data(i + 16)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 17)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 18)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 19)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 20)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 21)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 22)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 23)))); - let w03 = nat32To64(nat16To32(nat8To16(data(i + 24)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 25)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 26)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 27)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 28)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 29)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 30)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 31)))); - let w04 = nat32To64(nat16To32(nat8To16(data(i + 32)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 33)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 34)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 35)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 36)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 37)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 38)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 39)))); - let w05 = nat32To64(nat16To32(nat8To16(data(i + 40)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 41)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 42)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 43)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 44)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 45)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 46)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 47)))); - let w06 = nat32To64(nat16To32(nat8To16(data(i + 48)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 49)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 50)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 51)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 52)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 53)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 54)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 55)))); - let w07 = nat32To64(nat16To32(nat8To16(data(i + 56)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 57)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 58)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 59)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 60)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 61)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 62)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 63)))); - let w08 = nat32To64(nat16To32(nat8To16(data(i + 64)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 65)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 66)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 67)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 68)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 69)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 70)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 71)))); - let w09 = nat32To64(nat16To32(nat8To16(data(i + 72)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 73)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 74)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 75)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 76)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 77)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 78)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 79)))); - let w10 = nat32To64(nat16To32(nat8To16(data(i + 80)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 81)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 82)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 83)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 84)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 85)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 86)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 87)))); - let w11 = nat32To64(nat16To32(nat8To16(data(i + 88)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 89)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 90)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 91)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 92)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 93)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 94)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 95)))); - let w12 = nat32To64(nat16To32(nat8To16(data(i + 96)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 97)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 98)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 99)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 100)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 101)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 102)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 103)))); - let w13 = nat32To64(nat16To32(nat8To16(data(i + 104)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 105)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 106)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 107)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 108)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 109)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 110)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 111)))); - let w14 = nat32To64(nat16To32(nat8To16(data(i + 112)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 113)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 114)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 115)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 116)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 117)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 118)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 119)))); - let w15 = nat32To64(nat16To32(nat8To16(data(i + 120)))) << 56 | nat32To64(nat16To32(nat8To16(data(i + 121)))) << 48 | nat32To64(nat16To32(nat8To16(data(i + 122)))) << 40 | nat32To64(nat16To32(nat8To16(data(i + 123)))) << 32 | nat32To64(nat16To32(nat8To16(data(i + 124)))) << 24 | nat32To64(nat16To32(nat8To16(data(i + 125)))) << 16 | nat32To64(nat16To32(nat8To16(data(i + 126)))) << 8 | nat32To64(nat16To32(nat8To16(data(i + 127)))); - - let w16 = w00 +% rot(w01, 01) ^ rot(w01, 08) ^ (w01 >> 07) +% w09 +% rot(w14, 19) ^ rot(w14, 61) ^ (w14 >> 06); - let w17 = w01 +% rot(w02, 01) ^ rot(w02, 08) ^ (w02 >> 07) +% w10 +% rot(w15, 19) ^ rot(w15, 61) ^ (w15 >> 06); - let w18 = w02 +% rot(w03, 01) ^ rot(w03, 08) ^ (w03 >> 07) +% w11 +% rot(w16, 19) ^ rot(w16, 61) ^ (w16 >> 06); - let w19 = w03 +% rot(w04, 01) ^ rot(w04, 08) ^ (w04 >> 07) +% w12 +% rot(w17, 19) ^ rot(w17, 61) ^ (w17 >> 06); - let w20 = w04 +% rot(w05, 01) ^ rot(w05, 08) ^ (w05 >> 07) +% w13 +% rot(w18, 19) ^ rot(w18, 61) ^ (w18 >> 06); - let w21 = w05 +% rot(w06, 01) ^ rot(w06, 08) ^ (w06 >> 07) +% w14 +% rot(w19, 19) ^ rot(w19, 61) ^ (w19 >> 06); - let w22 = w06 +% rot(w07, 01) ^ rot(w07, 08) ^ (w07 >> 07) +% w15 +% rot(w20, 19) ^ rot(w20, 61) ^ (w20 >> 06); - let w23 = w07 +% rot(w08, 01) ^ rot(w08, 08) ^ (w08 >> 07) +% w16 +% rot(w21, 19) ^ rot(w21, 61) ^ (w21 >> 06); - let w24 = w08 +% rot(w09, 01) ^ rot(w09, 08) ^ (w09 >> 07) +% w17 +% rot(w22, 19) ^ rot(w22, 61) ^ (w22 >> 06); - let w25 = w09 +% rot(w10, 01) ^ rot(w10, 08) ^ (w10 >> 07) +% w18 +% rot(w23, 19) ^ rot(w23, 61) ^ (w23 >> 06); - let w26 = w10 +% rot(w11, 01) ^ rot(w11, 08) ^ (w11 >> 07) +% w19 +% rot(w24, 19) ^ rot(w24, 61) ^ (w24 >> 06); - let w27 = w11 +% rot(w12, 01) ^ rot(w12, 08) ^ (w12 >> 07) +% w20 +% rot(w25, 19) ^ rot(w25, 61) ^ (w25 >> 06); - let w28 = w12 +% rot(w13, 01) ^ rot(w13, 08) ^ (w13 >> 07) +% w21 +% rot(w26, 19) ^ rot(w26, 61) ^ (w26 >> 06); - let w29 = w13 +% rot(w14, 01) ^ rot(w14, 08) ^ (w14 >> 07) +% w22 +% rot(w27, 19) ^ rot(w27, 61) ^ (w27 >> 06); - let w30 = w14 +% rot(w15, 01) ^ rot(w15, 08) ^ (w15 >> 07) +% w23 +% rot(w28, 19) ^ rot(w28, 61) ^ (w28 >> 06); - let w31 = w15 +% rot(w16, 01) ^ rot(w16, 08) ^ (w16 >> 07) +% w24 +% rot(w29, 19) ^ rot(w29, 61) ^ (w29 >> 06); - let w32 = w16 +% rot(w17, 01) ^ rot(w17, 08) ^ (w17 >> 07) +% w25 +% rot(w30, 19) ^ rot(w30, 61) ^ (w30 >> 06); - let w33 = w17 +% rot(w18, 01) ^ rot(w18, 08) ^ (w18 >> 07) +% w26 +% rot(w31, 19) ^ rot(w31, 61) ^ (w31 >> 06); - let w34 = w18 +% rot(w19, 01) ^ rot(w19, 08) ^ (w19 >> 07) +% w27 +% rot(w32, 19) ^ rot(w32, 61) ^ (w32 >> 06); - let w35 = w19 +% rot(w20, 01) ^ rot(w20, 08) ^ (w20 >> 07) +% w28 +% rot(w33, 19) ^ rot(w33, 61) ^ (w33 >> 06); - let w36 = w20 +% rot(w21, 01) ^ rot(w21, 08) ^ (w21 >> 07) +% w29 +% rot(w34, 19) ^ rot(w34, 61) ^ (w34 >> 06); - let w37 = w21 +% rot(w22, 01) ^ rot(w22, 08) ^ (w22 >> 07) +% w30 +% rot(w35, 19) ^ rot(w35, 61) ^ (w35 >> 06); - let w38 = w22 +% rot(w23, 01) ^ rot(w23, 08) ^ (w23 >> 07) +% w31 +% rot(w36, 19) ^ rot(w36, 61) ^ (w36 >> 06); - let w39 = w23 +% rot(w24, 01) ^ rot(w24, 08) ^ (w24 >> 07) +% w32 +% rot(w37, 19) ^ rot(w37, 61) ^ (w37 >> 06); - let w40 = w24 +% rot(w25, 01) ^ rot(w25, 08) ^ (w25 >> 07) +% w33 +% rot(w38, 19) ^ rot(w38, 61) ^ (w38 >> 06); - let w41 = w25 +% rot(w26, 01) ^ rot(w26, 08) ^ (w26 >> 07) +% w34 +% rot(w39, 19) ^ rot(w39, 61) ^ (w39 >> 06); - let w42 = w26 +% rot(w27, 01) ^ rot(w27, 08) ^ (w27 >> 07) +% w35 +% rot(w40, 19) ^ rot(w40, 61) ^ (w40 >> 06); - let w43 = w27 +% rot(w28, 01) ^ rot(w28, 08) ^ (w28 >> 07) +% w36 +% rot(w41, 19) ^ rot(w41, 61) ^ (w41 >> 06); - let w44 = w28 +% rot(w29, 01) ^ rot(w29, 08) ^ (w29 >> 07) +% w37 +% rot(w42, 19) ^ rot(w42, 61) ^ (w42 >> 06); - let w45 = w29 +% rot(w30, 01) ^ rot(w30, 08) ^ (w30 >> 07) +% w38 +% rot(w43, 19) ^ rot(w43, 61) ^ (w43 >> 06); - let w46 = w30 +% rot(w31, 01) ^ rot(w31, 08) ^ (w31 >> 07) +% w39 +% rot(w44, 19) ^ rot(w44, 61) ^ (w44 >> 06); - let w47 = w31 +% rot(w32, 01) ^ rot(w32, 08) ^ (w32 >> 07) +% w40 +% rot(w45, 19) ^ rot(w45, 61) ^ (w45 >> 06); - let w48 = w32 +% rot(w33, 01) ^ rot(w33, 08) ^ (w33 >> 07) +% w41 +% rot(w46, 19) ^ rot(w46, 61) ^ (w46 >> 06); - let w49 = w33 +% rot(w34, 01) ^ rot(w34, 08) ^ (w34 >> 07) +% w42 +% rot(w47, 19) ^ rot(w47, 61) ^ (w47 >> 06); - let w50 = w34 +% rot(w35, 01) ^ rot(w35, 08) ^ (w35 >> 07) +% w43 +% rot(w48, 19) ^ rot(w48, 61) ^ (w48 >> 06); - let w51 = w35 +% rot(w36, 01) ^ rot(w36, 08) ^ (w36 >> 07) +% w44 +% rot(w49, 19) ^ rot(w49, 61) ^ (w49 >> 06); - let w52 = w36 +% rot(w37, 01) ^ rot(w37, 08) ^ (w37 >> 07) +% w45 +% rot(w50, 19) ^ rot(w50, 61) ^ (w50 >> 06); - let w53 = w37 +% rot(w38, 01) ^ rot(w38, 08) ^ (w38 >> 07) +% w46 +% rot(w51, 19) ^ rot(w51, 61) ^ (w51 >> 06); - let w54 = w38 +% rot(w39, 01) ^ rot(w39, 08) ^ (w39 >> 07) +% w47 +% rot(w52, 19) ^ rot(w52, 61) ^ (w52 >> 06); - let w55 = w39 +% rot(w40, 01) ^ rot(w40, 08) ^ (w40 >> 07) +% w48 +% rot(w53, 19) ^ rot(w53, 61) ^ (w53 >> 06); - let w56 = w40 +% rot(w41, 01) ^ rot(w41, 08) ^ (w41 >> 07) +% w49 +% rot(w54, 19) ^ rot(w54, 61) ^ (w54 >> 06); - let w57 = w41 +% rot(w42, 01) ^ rot(w42, 08) ^ (w42 >> 07) +% w50 +% rot(w55, 19) ^ rot(w55, 61) ^ (w55 >> 06); - let w58 = w42 +% rot(w43, 01) ^ rot(w43, 08) ^ (w43 >> 07) +% w51 +% rot(w56, 19) ^ rot(w56, 61) ^ (w56 >> 06); - let w59 = w43 +% rot(w44, 01) ^ rot(w44, 08) ^ (w44 >> 07) +% w52 +% rot(w57, 19) ^ rot(w57, 61) ^ (w57 >> 06); - let w60 = w44 +% rot(w45, 01) ^ rot(w45, 08) ^ (w45 >> 07) +% w53 +% rot(w58, 19) ^ rot(w58, 61) ^ (w58 >> 06); - let w61 = w45 +% rot(w46, 01) ^ rot(w46, 08) ^ (w46 >> 07) +% w54 +% rot(w59, 19) ^ rot(w59, 61) ^ (w59 >> 06); - let w62 = w46 +% rot(w47, 01) ^ rot(w47, 08) ^ (w47 >> 07) +% w55 +% rot(w60, 19) ^ rot(w60, 61) ^ (w60 >> 06); - let w63 = w47 +% rot(w48, 01) ^ rot(w48, 08) ^ (w48 >> 07) +% w56 +% rot(w61, 19) ^ rot(w61, 61) ^ (w61 >> 06); - let w64 = w48 +% rot(w49, 01) ^ rot(w49, 08) ^ (w49 >> 07) +% w57 +% rot(w62, 19) ^ rot(w62, 61) ^ (w62 >> 06); - let w65 = w49 +% rot(w50, 01) ^ rot(w50, 08) ^ (w50 >> 07) +% w58 +% rot(w63, 19) ^ rot(w63, 61) ^ (w63 >> 06); - let w66 = w50 +% rot(w51, 01) ^ rot(w51, 08) ^ (w51 >> 07) +% w59 +% rot(w64, 19) ^ rot(w64, 61) ^ (w64 >> 06); - let w67 = w51 +% rot(w52, 01) ^ rot(w52, 08) ^ (w52 >> 07) +% w60 +% rot(w65, 19) ^ rot(w65, 61) ^ (w65 >> 06); - let w68 = w52 +% rot(w53, 01) ^ rot(w53, 08) ^ (w53 >> 07) +% w61 +% rot(w66, 19) ^ rot(w66, 61) ^ (w66 >> 06); - let w69 = w53 +% rot(w54, 01) ^ rot(w54, 08) ^ (w54 >> 07) +% w62 +% rot(w67, 19) ^ rot(w67, 61) ^ (w67 >> 06); - let w70 = w54 +% rot(w55, 01) ^ rot(w55, 08) ^ (w55 >> 07) +% w63 +% rot(w68, 19) ^ rot(w68, 61) ^ (w68 >> 06); - let w71 = w55 +% rot(w56, 01) ^ rot(w56, 08) ^ (w56 >> 07) +% w64 +% rot(w69, 19) ^ rot(w69, 61) ^ (w69 >> 06); - let w72 = w56 +% rot(w57, 01) ^ rot(w57, 08) ^ (w57 >> 07) +% w65 +% rot(w70, 19) ^ rot(w70, 61) ^ (w70 >> 06); - let w73 = w57 +% rot(w58, 01) ^ rot(w58, 08) ^ (w58 >> 07) +% w66 +% rot(w71, 19) ^ rot(w71, 61) ^ (w71 >> 06); - let w74 = w58 +% rot(w59, 01) ^ rot(w59, 08) ^ (w59 >> 07) +% w67 +% rot(w72, 19) ^ rot(w72, 61) ^ (w72 >> 06); - let w75 = w59 +% rot(w60, 01) ^ rot(w60, 08) ^ (w60 >> 07) +% w68 +% rot(w73, 19) ^ rot(w73, 61) ^ (w73 >> 06); - let w76 = w60 +% rot(w61, 01) ^ rot(w61, 08) ^ (w61 >> 07) +% w69 +% rot(w74, 19) ^ rot(w74, 61) ^ (w74 >> 06); - let w77 = w61 +% rot(w62, 01) ^ rot(w62, 08) ^ (w62 >> 07) +% w70 +% rot(w75, 19) ^ rot(w75, 61) ^ (w75 >> 06); - let w78 = w62 +% rot(w63, 01) ^ rot(w63, 08) ^ (w63 >> 07) +% w71 +% rot(w76, 19) ^ rot(w76, 61) ^ (w76 >> 06); - let w79 = w63 +% rot(w64, 01) ^ rot(w64, 08) ^ (w64 >> 07) +% w72 +% rot(w77, 19) ^ rot(w77, 61) ^ (w77 >> 06); - - // prettier-ignore - do { - t := h +% K.K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K09 +% w09 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K10 +% w10 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K11 +% w11 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K12 +% w12 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K13 +% w13 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K14 +% w14 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K64 +% w64 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K65 +% w65 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K66 +% w66 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K67 +% w67 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K68 +% w68 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K69 +% w69 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K70 +% w70 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K71 +% w71 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K72 +% w72 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K73 +% w73 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K74 +% w74 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K75 +% w75 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K76 +% w76 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K77 +% w77 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K78 +% w78 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K79 +% w79 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - }; - - // final addition - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - // counters - i += 128; - }; - // write state back to registers - state[0] := a; - state[1] := b; - state[2] := c; - state[3] := d; - state[4] := e; - state[5] := f; - state[6] := g; - state[7] := h; - - return i; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/whole_blocks/array.mo b/.mops/sha2@0.2.5/src/sha512/whole_blocks/array.mo deleted file mode 100644 index 8701908..0000000 --- a/.mops/sha2@0.2.5/src/sha512/whole_blocks/array.mo +++ /dev/null @@ -1,227 +0,0 @@ -import Prim "mo:prim"; -import K "../constants"; - -module { - func rot(x : Nat64, y : Nat64) : Nat64 = x <>> y; - - let nat32To64 = Prim.nat32ToNat64; - let nat16To32 = Prim.nat16ToNat32; - let nat8To16 = Prim.nat8ToNat16; - - /// Run the SHA512 compression on every full 128-byte block in `data` from index `start` to the end. Returns the index just past the last block consumed (i.e. `start + 128 * blocks`). - public func process_blocks(state : [var Nat64], data : [Nat8], start : Nat) : Nat { - let sz = data.size(); - var i = start; - // load state registers - var a = state[0]; - var b = state[1]; - var c = state[2]; - var d = state[3]; - var e = state[4]; - var f = state[5]; - var g = state[6]; - var h = state[7]; - var t = 0 : Nat64; - var i_max : Nat = i + ((sz - i) / 128) * 128; - while (i < i_max) { - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - - let w00 = nat32To64(nat16To32(nat8To16(data[i + 0]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 1]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 2]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 3]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 4]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 5]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 6]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 7]))); - let w01 = nat32To64(nat16To32(nat8To16(data[i + 8]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 9]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 10]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 11]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 12]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 13]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 14]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 15]))); - let w02 = nat32To64(nat16To32(nat8To16(data[i + 16]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 17]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 18]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 19]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 20]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 21]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 22]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 23]))); - let w03 = nat32To64(nat16To32(nat8To16(data[i + 24]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 25]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 26]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 27]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 28]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 29]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 30]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 31]))); - let w04 = nat32To64(nat16To32(nat8To16(data[i + 32]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 33]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 34]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 35]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 36]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 37]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 38]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 39]))); - let w05 = nat32To64(nat16To32(nat8To16(data[i + 40]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 41]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 42]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 43]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 44]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 45]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 46]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 47]))); - let w06 = nat32To64(nat16To32(nat8To16(data[i + 48]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 49]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 50]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 51]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 52]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 53]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 54]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 55]))); - let w07 = nat32To64(nat16To32(nat8To16(data[i + 56]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 57]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 58]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 59]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 60]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 61]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 62]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 63]))); - let w08 = nat32To64(nat16To32(nat8To16(data[i + 64]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 65]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 66]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 67]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 68]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 69]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 70]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 71]))); - let w09 = nat32To64(nat16To32(nat8To16(data[i + 72]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 73]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 74]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 75]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 76]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 77]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 78]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 79]))); - let w10 = nat32To64(nat16To32(nat8To16(data[i + 80]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 81]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 82]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 83]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 84]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 85]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 86]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 87]))); - let w11 = nat32To64(nat16To32(nat8To16(data[i + 88]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 89]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 90]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 91]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 92]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 93]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 94]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 95]))); - let w12 = nat32To64(nat16To32(nat8To16(data[i + 96]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 97]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 98]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 99]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 100]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 101]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 102]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 103]))); - let w13 = nat32To64(nat16To32(nat8To16(data[i + 104]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 105]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 106]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 107]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 108]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 109]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 110]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 111]))); - let w14 = nat32To64(nat16To32(nat8To16(data[i + 112]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 113]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 114]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 115]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 116]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 117]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 118]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 119]))); - let w15 = nat32To64(nat16To32(nat8To16(data[i + 120]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 121]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 122]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 123]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 124]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 125]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 126]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 127]))); - - let w16 = w00 +% rot(w01, 01) ^ rot(w01, 08) ^ (w01 >> 07) +% w09 +% rot(w14, 19) ^ rot(w14, 61) ^ (w14 >> 06); - let w17 = w01 +% rot(w02, 01) ^ rot(w02, 08) ^ (w02 >> 07) +% w10 +% rot(w15, 19) ^ rot(w15, 61) ^ (w15 >> 06); - let w18 = w02 +% rot(w03, 01) ^ rot(w03, 08) ^ (w03 >> 07) +% w11 +% rot(w16, 19) ^ rot(w16, 61) ^ (w16 >> 06); - let w19 = w03 +% rot(w04, 01) ^ rot(w04, 08) ^ (w04 >> 07) +% w12 +% rot(w17, 19) ^ rot(w17, 61) ^ (w17 >> 06); - let w20 = w04 +% rot(w05, 01) ^ rot(w05, 08) ^ (w05 >> 07) +% w13 +% rot(w18, 19) ^ rot(w18, 61) ^ (w18 >> 06); - let w21 = w05 +% rot(w06, 01) ^ rot(w06, 08) ^ (w06 >> 07) +% w14 +% rot(w19, 19) ^ rot(w19, 61) ^ (w19 >> 06); - let w22 = w06 +% rot(w07, 01) ^ rot(w07, 08) ^ (w07 >> 07) +% w15 +% rot(w20, 19) ^ rot(w20, 61) ^ (w20 >> 06); - let w23 = w07 +% rot(w08, 01) ^ rot(w08, 08) ^ (w08 >> 07) +% w16 +% rot(w21, 19) ^ rot(w21, 61) ^ (w21 >> 06); - let w24 = w08 +% rot(w09, 01) ^ rot(w09, 08) ^ (w09 >> 07) +% w17 +% rot(w22, 19) ^ rot(w22, 61) ^ (w22 >> 06); - let w25 = w09 +% rot(w10, 01) ^ rot(w10, 08) ^ (w10 >> 07) +% w18 +% rot(w23, 19) ^ rot(w23, 61) ^ (w23 >> 06); - let w26 = w10 +% rot(w11, 01) ^ rot(w11, 08) ^ (w11 >> 07) +% w19 +% rot(w24, 19) ^ rot(w24, 61) ^ (w24 >> 06); - let w27 = w11 +% rot(w12, 01) ^ rot(w12, 08) ^ (w12 >> 07) +% w20 +% rot(w25, 19) ^ rot(w25, 61) ^ (w25 >> 06); - let w28 = w12 +% rot(w13, 01) ^ rot(w13, 08) ^ (w13 >> 07) +% w21 +% rot(w26, 19) ^ rot(w26, 61) ^ (w26 >> 06); - let w29 = w13 +% rot(w14, 01) ^ rot(w14, 08) ^ (w14 >> 07) +% w22 +% rot(w27, 19) ^ rot(w27, 61) ^ (w27 >> 06); - let w30 = w14 +% rot(w15, 01) ^ rot(w15, 08) ^ (w15 >> 07) +% w23 +% rot(w28, 19) ^ rot(w28, 61) ^ (w28 >> 06); - let w31 = w15 +% rot(w16, 01) ^ rot(w16, 08) ^ (w16 >> 07) +% w24 +% rot(w29, 19) ^ rot(w29, 61) ^ (w29 >> 06); - let w32 = w16 +% rot(w17, 01) ^ rot(w17, 08) ^ (w17 >> 07) +% w25 +% rot(w30, 19) ^ rot(w30, 61) ^ (w30 >> 06); - let w33 = w17 +% rot(w18, 01) ^ rot(w18, 08) ^ (w18 >> 07) +% w26 +% rot(w31, 19) ^ rot(w31, 61) ^ (w31 >> 06); - let w34 = w18 +% rot(w19, 01) ^ rot(w19, 08) ^ (w19 >> 07) +% w27 +% rot(w32, 19) ^ rot(w32, 61) ^ (w32 >> 06); - let w35 = w19 +% rot(w20, 01) ^ rot(w20, 08) ^ (w20 >> 07) +% w28 +% rot(w33, 19) ^ rot(w33, 61) ^ (w33 >> 06); - let w36 = w20 +% rot(w21, 01) ^ rot(w21, 08) ^ (w21 >> 07) +% w29 +% rot(w34, 19) ^ rot(w34, 61) ^ (w34 >> 06); - let w37 = w21 +% rot(w22, 01) ^ rot(w22, 08) ^ (w22 >> 07) +% w30 +% rot(w35, 19) ^ rot(w35, 61) ^ (w35 >> 06); - let w38 = w22 +% rot(w23, 01) ^ rot(w23, 08) ^ (w23 >> 07) +% w31 +% rot(w36, 19) ^ rot(w36, 61) ^ (w36 >> 06); - let w39 = w23 +% rot(w24, 01) ^ rot(w24, 08) ^ (w24 >> 07) +% w32 +% rot(w37, 19) ^ rot(w37, 61) ^ (w37 >> 06); - let w40 = w24 +% rot(w25, 01) ^ rot(w25, 08) ^ (w25 >> 07) +% w33 +% rot(w38, 19) ^ rot(w38, 61) ^ (w38 >> 06); - let w41 = w25 +% rot(w26, 01) ^ rot(w26, 08) ^ (w26 >> 07) +% w34 +% rot(w39, 19) ^ rot(w39, 61) ^ (w39 >> 06); - let w42 = w26 +% rot(w27, 01) ^ rot(w27, 08) ^ (w27 >> 07) +% w35 +% rot(w40, 19) ^ rot(w40, 61) ^ (w40 >> 06); - let w43 = w27 +% rot(w28, 01) ^ rot(w28, 08) ^ (w28 >> 07) +% w36 +% rot(w41, 19) ^ rot(w41, 61) ^ (w41 >> 06); - let w44 = w28 +% rot(w29, 01) ^ rot(w29, 08) ^ (w29 >> 07) +% w37 +% rot(w42, 19) ^ rot(w42, 61) ^ (w42 >> 06); - let w45 = w29 +% rot(w30, 01) ^ rot(w30, 08) ^ (w30 >> 07) +% w38 +% rot(w43, 19) ^ rot(w43, 61) ^ (w43 >> 06); - let w46 = w30 +% rot(w31, 01) ^ rot(w31, 08) ^ (w31 >> 07) +% w39 +% rot(w44, 19) ^ rot(w44, 61) ^ (w44 >> 06); - let w47 = w31 +% rot(w32, 01) ^ rot(w32, 08) ^ (w32 >> 07) +% w40 +% rot(w45, 19) ^ rot(w45, 61) ^ (w45 >> 06); - let w48 = w32 +% rot(w33, 01) ^ rot(w33, 08) ^ (w33 >> 07) +% w41 +% rot(w46, 19) ^ rot(w46, 61) ^ (w46 >> 06); - let w49 = w33 +% rot(w34, 01) ^ rot(w34, 08) ^ (w34 >> 07) +% w42 +% rot(w47, 19) ^ rot(w47, 61) ^ (w47 >> 06); - let w50 = w34 +% rot(w35, 01) ^ rot(w35, 08) ^ (w35 >> 07) +% w43 +% rot(w48, 19) ^ rot(w48, 61) ^ (w48 >> 06); - let w51 = w35 +% rot(w36, 01) ^ rot(w36, 08) ^ (w36 >> 07) +% w44 +% rot(w49, 19) ^ rot(w49, 61) ^ (w49 >> 06); - let w52 = w36 +% rot(w37, 01) ^ rot(w37, 08) ^ (w37 >> 07) +% w45 +% rot(w50, 19) ^ rot(w50, 61) ^ (w50 >> 06); - let w53 = w37 +% rot(w38, 01) ^ rot(w38, 08) ^ (w38 >> 07) +% w46 +% rot(w51, 19) ^ rot(w51, 61) ^ (w51 >> 06); - let w54 = w38 +% rot(w39, 01) ^ rot(w39, 08) ^ (w39 >> 07) +% w47 +% rot(w52, 19) ^ rot(w52, 61) ^ (w52 >> 06); - let w55 = w39 +% rot(w40, 01) ^ rot(w40, 08) ^ (w40 >> 07) +% w48 +% rot(w53, 19) ^ rot(w53, 61) ^ (w53 >> 06); - let w56 = w40 +% rot(w41, 01) ^ rot(w41, 08) ^ (w41 >> 07) +% w49 +% rot(w54, 19) ^ rot(w54, 61) ^ (w54 >> 06); - let w57 = w41 +% rot(w42, 01) ^ rot(w42, 08) ^ (w42 >> 07) +% w50 +% rot(w55, 19) ^ rot(w55, 61) ^ (w55 >> 06); - let w58 = w42 +% rot(w43, 01) ^ rot(w43, 08) ^ (w43 >> 07) +% w51 +% rot(w56, 19) ^ rot(w56, 61) ^ (w56 >> 06); - let w59 = w43 +% rot(w44, 01) ^ rot(w44, 08) ^ (w44 >> 07) +% w52 +% rot(w57, 19) ^ rot(w57, 61) ^ (w57 >> 06); - let w60 = w44 +% rot(w45, 01) ^ rot(w45, 08) ^ (w45 >> 07) +% w53 +% rot(w58, 19) ^ rot(w58, 61) ^ (w58 >> 06); - let w61 = w45 +% rot(w46, 01) ^ rot(w46, 08) ^ (w46 >> 07) +% w54 +% rot(w59, 19) ^ rot(w59, 61) ^ (w59 >> 06); - let w62 = w46 +% rot(w47, 01) ^ rot(w47, 08) ^ (w47 >> 07) +% w55 +% rot(w60, 19) ^ rot(w60, 61) ^ (w60 >> 06); - let w63 = w47 +% rot(w48, 01) ^ rot(w48, 08) ^ (w48 >> 07) +% w56 +% rot(w61, 19) ^ rot(w61, 61) ^ (w61 >> 06); - let w64 = w48 +% rot(w49, 01) ^ rot(w49, 08) ^ (w49 >> 07) +% w57 +% rot(w62, 19) ^ rot(w62, 61) ^ (w62 >> 06); - let w65 = w49 +% rot(w50, 01) ^ rot(w50, 08) ^ (w50 >> 07) +% w58 +% rot(w63, 19) ^ rot(w63, 61) ^ (w63 >> 06); - let w66 = w50 +% rot(w51, 01) ^ rot(w51, 08) ^ (w51 >> 07) +% w59 +% rot(w64, 19) ^ rot(w64, 61) ^ (w64 >> 06); - let w67 = w51 +% rot(w52, 01) ^ rot(w52, 08) ^ (w52 >> 07) +% w60 +% rot(w65, 19) ^ rot(w65, 61) ^ (w65 >> 06); - let w68 = w52 +% rot(w53, 01) ^ rot(w53, 08) ^ (w53 >> 07) +% w61 +% rot(w66, 19) ^ rot(w66, 61) ^ (w66 >> 06); - let w69 = w53 +% rot(w54, 01) ^ rot(w54, 08) ^ (w54 >> 07) +% w62 +% rot(w67, 19) ^ rot(w67, 61) ^ (w67 >> 06); - let w70 = w54 +% rot(w55, 01) ^ rot(w55, 08) ^ (w55 >> 07) +% w63 +% rot(w68, 19) ^ rot(w68, 61) ^ (w68 >> 06); - let w71 = w55 +% rot(w56, 01) ^ rot(w56, 08) ^ (w56 >> 07) +% w64 +% rot(w69, 19) ^ rot(w69, 61) ^ (w69 >> 06); - let w72 = w56 +% rot(w57, 01) ^ rot(w57, 08) ^ (w57 >> 07) +% w65 +% rot(w70, 19) ^ rot(w70, 61) ^ (w70 >> 06); - let w73 = w57 +% rot(w58, 01) ^ rot(w58, 08) ^ (w58 >> 07) +% w66 +% rot(w71, 19) ^ rot(w71, 61) ^ (w71 >> 06); - let w74 = w58 +% rot(w59, 01) ^ rot(w59, 08) ^ (w59 >> 07) +% w67 +% rot(w72, 19) ^ rot(w72, 61) ^ (w72 >> 06); - let w75 = w59 +% rot(w60, 01) ^ rot(w60, 08) ^ (w60 >> 07) +% w68 +% rot(w73, 19) ^ rot(w73, 61) ^ (w73 >> 06); - let w76 = w60 +% rot(w61, 01) ^ rot(w61, 08) ^ (w61 >> 07) +% w69 +% rot(w74, 19) ^ rot(w74, 61) ^ (w74 >> 06); - let w77 = w61 +% rot(w62, 01) ^ rot(w62, 08) ^ (w62 >> 07) +% w70 +% rot(w75, 19) ^ rot(w75, 61) ^ (w75 >> 06); - let w78 = w62 +% rot(w63, 01) ^ rot(w63, 08) ^ (w63 >> 07) +% w71 +% rot(w76, 19) ^ rot(w76, 61) ^ (w76 >> 06); - let w79 = w63 +% rot(w64, 01) ^ rot(w64, 08) ^ (w64 >> 07) +% w72 +% rot(w77, 19) ^ rot(w77, 61) ^ (w77 >> 06); - - // prettier-ignore - do { - t := h +% K.K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K09 +% w09 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K10 +% w10 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K11 +% w11 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K12 +% w12 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K13 +% w13 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K14 +% w14 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K64 +% w64 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K65 +% w65 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K66 +% w66 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K67 +% w67 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K68 +% w68 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K69 +% w69 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K70 +% w70 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K71 +% w71 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K72 +% w72 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K73 +% w73 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K74 +% w74 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K75 +% w75 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K76 +% w76 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K77 +% w77 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K78 +% w78 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K79 +% w79 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - }; - - // final addition - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - // counters - i += 128; - }; - // write state back to registers - state[0] := a; - state[1] := b; - state[2] := c; - state[3] := d; - state[4] := e; - state[5] := f; - state[6] := g; - state[7] := h; - - return i; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/whole_blocks/blob.mo b/.mops/sha2@0.2.5/src/sha512/whole_blocks/blob.mo deleted file mode 100644 index a1cecf6..0000000 --- a/.mops/sha2@0.2.5/src/sha512/whole_blocks/blob.mo +++ /dev/null @@ -1,227 +0,0 @@ -import Prim "mo:prim"; -import K "../constants"; - -module { - func rot(x : Nat64, y : Nat64) : Nat64 = x <>> y; - - let nat32To64 = Prim.nat32ToNat64; - let nat16To32 = Prim.nat16ToNat32; - let nat8To16 = Prim.nat8ToNat16; - - /// Run the SHA512 compression on every full 128-byte block in `data` from index `start` to the end. Returns the index just past the last block consumed (i.e. `start + 128 * blocks`). - public func process_blocks(state : [var Nat64], data : Blob, start : Nat) : Nat { - let sz = data.size(); - var i = start; - // load state registers - var a = state[0]; - var b = state[1]; - var c = state[2]; - var d = state[3]; - var e = state[4]; - var f = state[5]; - var g = state[6]; - var h = state[7]; - var t = 0 : Nat64; - var i_max : Nat = i + ((sz - i) / 128) * 128; - while (i < i_max) { - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - - let w00 = nat32To64(nat16To32(nat8To16(data[i + 0]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 1]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 2]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 3]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 4]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 5]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 6]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 7]))); - let w01 = nat32To64(nat16To32(nat8To16(data[i + 8]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 9]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 10]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 11]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 12]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 13]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 14]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 15]))); - let w02 = nat32To64(nat16To32(nat8To16(data[i + 16]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 17]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 18]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 19]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 20]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 21]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 22]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 23]))); - let w03 = nat32To64(nat16To32(nat8To16(data[i + 24]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 25]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 26]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 27]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 28]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 29]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 30]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 31]))); - let w04 = nat32To64(nat16To32(nat8To16(data[i + 32]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 33]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 34]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 35]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 36]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 37]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 38]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 39]))); - let w05 = nat32To64(nat16To32(nat8To16(data[i + 40]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 41]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 42]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 43]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 44]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 45]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 46]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 47]))); - let w06 = nat32To64(nat16To32(nat8To16(data[i + 48]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 49]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 50]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 51]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 52]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 53]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 54]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 55]))); - let w07 = nat32To64(nat16To32(nat8To16(data[i + 56]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 57]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 58]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 59]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 60]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 61]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 62]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 63]))); - let w08 = nat32To64(nat16To32(nat8To16(data[i + 64]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 65]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 66]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 67]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 68]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 69]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 70]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 71]))); - let w09 = nat32To64(nat16To32(nat8To16(data[i + 72]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 73]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 74]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 75]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 76]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 77]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 78]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 79]))); - let w10 = nat32To64(nat16To32(nat8To16(data[i + 80]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 81]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 82]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 83]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 84]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 85]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 86]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 87]))); - let w11 = nat32To64(nat16To32(nat8To16(data[i + 88]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 89]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 90]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 91]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 92]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 93]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 94]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 95]))); - let w12 = nat32To64(nat16To32(nat8To16(data[i + 96]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 97]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 98]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 99]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 100]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 101]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 102]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 103]))); - let w13 = nat32To64(nat16To32(nat8To16(data[i + 104]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 105]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 106]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 107]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 108]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 109]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 110]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 111]))); - let w14 = nat32To64(nat16To32(nat8To16(data[i + 112]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 113]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 114]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 115]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 116]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 117]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 118]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 119]))); - let w15 = nat32To64(nat16To32(nat8To16(data[i + 120]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 121]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 122]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 123]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 124]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 125]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 126]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 127]))); - - let w16 = w00 +% rot(w01, 01) ^ rot(w01, 08) ^ (w01 >> 07) +% w09 +% rot(w14, 19) ^ rot(w14, 61) ^ (w14 >> 06); - let w17 = w01 +% rot(w02, 01) ^ rot(w02, 08) ^ (w02 >> 07) +% w10 +% rot(w15, 19) ^ rot(w15, 61) ^ (w15 >> 06); - let w18 = w02 +% rot(w03, 01) ^ rot(w03, 08) ^ (w03 >> 07) +% w11 +% rot(w16, 19) ^ rot(w16, 61) ^ (w16 >> 06); - let w19 = w03 +% rot(w04, 01) ^ rot(w04, 08) ^ (w04 >> 07) +% w12 +% rot(w17, 19) ^ rot(w17, 61) ^ (w17 >> 06); - let w20 = w04 +% rot(w05, 01) ^ rot(w05, 08) ^ (w05 >> 07) +% w13 +% rot(w18, 19) ^ rot(w18, 61) ^ (w18 >> 06); - let w21 = w05 +% rot(w06, 01) ^ rot(w06, 08) ^ (w06 >> 07) +% w14 +% rot(w19, 19) ^ rot(w19, 61) ^ (w19 >> 06); - let w22 = w06 +% rot(w07, 01) ^ rot(w07, 08) ^ (w07 >> 07) +% w15 +% rot(w20, 19) ^ rot(w20, 61) ^ (w20 >> 06); - let w23 = w07 +% rot(w08, 01) ^ rot(w08, 08) ^ (w08 >> 07) +% w16 +% rot(w21, 19) ^ rot(w21, 61) ^ (w21 >> 06); - let w24 = w08 +% rot(w09, 01) ^ rot(w09, 08) ^ (w09 >> 07) +% w17 +% rot(w22, 19) ^ rot(w22, 61) ^ (w22 >> 06); - let w25 = w09 +% rot(w10, 01) ^ rot(w10, 08) ^ (w10 >> 07) +% w18 +% rot(w23, 19) ^ rot(w23, 61) ^ (w23 >> 06); - let w26 = w10 +% rot(w11, 01) ^ rot(w11, 08) ^ (w11 >> 07) +% w19 +% rot(w24, 19) ^ rot(w24, 61) ^ (w24 >> 06); - let w27 = w11 +% rot(w12, 01) ^ rot(w12, 08) ^ (w12 >> 07) +% w20 +% rot(w25, 19) ^ rot(w25, 61) ^ (w25 >> 06); - let w28 = w12 +% rot(w13, 01) ^ rot(w13, 08) ^ (w13 >> 07) +% w21 +% rot(w26, 19) ^ rot(w26, 61) ^ (w26 >> 06); - let w29 = w13 +% rot(w14, 01) ^ rot(w14, 08) ^ (w14 >> 07) +% w22 +% rot(w27, 19) ^ rot(w27, 61) ^ (w27 >> 06); - let w30 = w14 +% rot(w15, 01) ^ rot(w15, 08) ^ (w15 >> 07) +% w23 +% rot(w28, 19) ^ rot(w28, 61) ^ (w28 >> 06); - let w31 = w15 +% rot(w16, 01) ^ rot(w16, 08) ^ (w16 >> 07) +% w24 +% rot(w29, 19) ^ rot(w29, 61) ^ (w29 >> 06); - let w32 = w16 +% rot(w17, 01) ^ rot(w17, 08) ^ (w17 >> 07) +% w25 +% rot(w30, 19) ^ rot(w30, 61) ^ (w30 >> 06); - let w33 = w17 +% rot(w18, 01) ^ rot(w18, 08) ^ (w18 >> 07) +% w26 +% rot(w31, 19) ^ rot(w31, 61) ^ (w31 >> 06); - let w34 = w18 +% rot(w19, 01) ^ rot(w19, 08) ^ (w19 >> 07) +% w27 +% rot(w32, 19) ^ rot(w32, 61) ^ (w32 >> 06); - let w35 = w19 +% rot(w20, 01) ^ rot(w20, 08) ^ (w20 >> 07) +% w28 +% rot(w33, 19) ^ rot(w33, 61) ^ (w33 >> 06); - let w36 = w20 +% rot(w21, 01) ^ rot(w21, 08) ^ (w21 >> 07) +% w29 +% rot(w34, 19) ^ rot(w34, 61) ^ (w34 >> 06); - let w37 = w21 +% rot(w22, 01) ^ rot(w22, 08) ^ (w22 >> 07) +% w30 +% rot(w35, 19) ^ rot(w35, 61) ^ (w35 >> 06); - let w38 = w22 +% rot(w23, 01) ^ rot(w23, 08) ^ (w23 >> 07) +% w31 +% rot(w36, 19) ^ rot(w36, 61) ^ (w36 >> 06); - let w39 = w23 +% rot(w24, 01) ^ rot(w24, 08) ^ (w24 >> 07) +% w32 +% rot(w37, 19) ^ rot(w37, 61) ^ (w37 >> 06); - let w40 = w24 +% rot(w25, 01) ^ rot(w25, 08) ^ (w25 >> 07) +% w33 +% rot(w38, 19) ^ rot(w38, 61) ^ (w38 >> 06); - let w41 = w25 +% rot(w26, 01) ^ rot(w26, 08) ^ (w26 >> 07) +% w34 +% rot(w39, 19) ^ rot(w39, 61) ^ (w39 >> 06); - let w42 = w26 +% rot(w27, 01) ^ rot(w27, 08) ^ (w27 >> 07) +% w35 +% rot(w40, 19) ^ rot(w40, 61) ^ (w40 >> 06); - let w43 = w27 +% rot(w28, 01) ^ rot(w28, 08) ^ (w28 >> 07) +% w36 +% rot(w41, 19) ^ rot(w41, 61) ^ (w41 >> 06); - let w44 = w28 +% rot(w29, 01) ^ rot(w29, 08) ^ (w29 >> 07) +% w37 +% rot(w42, 19) ^ rot(w42, 61) ^ (w42 >> 06); - let w45 = w29 +% rot(w30, 01) ^ rot(w30, 08) ^ (w30 >> 07) +% w38 +% rot(w43, 19) ^ rot(w43, 61) ^ (w43 >> 06); - let w46 = w30 +% rot(w31, 01) ^ rot(w31, 08) ^ (w31 >> 07) +% w39 +% rot(w44, 19) ^ rot(w44, 61) ^ (w44 >> 06); - let w47 = w31 +% rot(w32, 01) ^ rot(w32, 08) ^ (w32 >> 07) +% w40 +% rot(w45, 19) ^ rot(w45, 61) ^ (w45 >> 06); - let w48 = w32 +% rot(w33, 01) ^ rot(w33, 08) ^ (w33 >> 07) +% w41 +% rot(w46, 19) ^ rot(w46, 61) ^ (w46 >> 06); - let w49 = w33 +% rot(w34, 01) ^ rot(w34, 08) ^ (w34 >> 07) +% w42 +% rot(w47, 19) ^ rot(w47, 61) ^ (w47 >> 06); - let w50 = w34 +% rot(w35, 01) ^ rot(w35, 08) ^ (w35 >> 07) +% w43 +% rot(w48, 19) ^ rot(w48, 61) ^ (w48 >> 06); - let w51 = w35 +% rot(w36, 01) ^ rot(w36, 08) ^ (w36 >> 07) +% w44 +% rot(w49, 19) ^ rot(w49, 61) ^ (w49 >> 06); - let w52 = w36 +% rot(w37, 01) ^ rot(w37, 08) ^ (w37 >> 07) +% w45 +% rot(w50, 19) ^ rot(w50, 61) ^ (w50 >> 06); - let w53 = w37 +% rot(w38, 01) ^ rot(w38, 08) ^ (w38 >> 07) +% w46 +% rot(w51, 19) ^ rot(w51, 61) ^ (w51 >> 06); - let w54 = w38 +% rot(w39, 01) ^ rot(w39, 08) ^ (w39 >> 07) +% w47 +% rot(w52, 19) ^ rot(w52, 61) ^ (w52 >> 06); - let w55 = w39 +% rot(w40, 01) ^ rot(w40, 08) ^ (w40 >> 07) +% w48 +% rot(w53, 19) ^ rot(w53, 61) ^ (w53 >> 06); - let w56 = w40 +% rot(w41, 01) ^ rot(w41, 08) ^ (w41 >> 07) +% w49 +% rot(w54, 19) ^ rot(w54, 61) ^ (w54 >> 06); - let w57 = w41 +% rot(w42, 01) ^ rot(w42, 08) ^ (w42 >> 07) +% w50 +% rot(w55, 19) ^ rot(w55, 61) ^ (w55 >> 06); - let w58 = w42 +% rot(w43, 01) ^ rot(w43, 08) ^ (w43 >> 07) +% w51 +% rot(w56, 19) ^ rot(w56, 61) ^ (w56 >> 06); - let w59 = w43 +% rot(w44, 01) ^ rot(w44, 08) ^ (w44 >> 07) +% w52 +% rot(w57, 19) ^ rot(w57, 61) ^ (w57 >> 06); - let w60 = w44 +% rot(w45, 01) ^ rot(w45, 08) ^ (w45 >> 07) +% w53 +% rot(w58, 19) ^ rot(w58, 61) ^ (w58 >> 06); - let w61 = w45 +% rot(w46, 01) ^ rot(w46, 08) ^ (w46 >> 07) +% w54 +% rot(w59, 19) ^ rot(w59, 61) ^ (w59 >> 06); - let w62 = w46 +% rot(w47, 01) ^ rot(w47, 08) ^ (w47 >> 07) +% w55 +% rot(w60, 19) ^ rot(w60, 61) ^ (w60 >> 06); - let w63 = w47 +% rot(w48, 01) ^ rot(w48, 08) ^ (w48 >> 07) +% w56 +% rot(w61, 19) ^ rot(w61, 61) ^ (w61 >> 06); - let w64 = w48 +% rot(w49, 01) ^ rot(w49, 08) ^ (w49 >> 07) +% w57 +% rot(w62, 19) ^ rot(w62, 61) ^ (w62 >> 06); - let w65 = w49 +% rot(w50, 01) ^ rot(w50, 08) ^ (w50 >> 07) +% w58 +% rot(w63, 19) ^ rot(w63, 61) ^ (w63 >> 06); - let w66 = w50 +% rot(w51, 01) ^ rot(w51, 08) ^ (w51 >> 07) +% w59 +% rot(w64, 19) ^ rot(w64, 61) ^ (w64 >> 06); - let w67 = w51 +% rot(w52, 01) ^ rot(w52, 08) ^ (w52 >> 07) +% w60 +% rot(w65, 19) ^ rot(w65, 61) ^ (w65 >> 06); - let w68 = w52 +% rot(w53, 01) ^ rot(w53, 08) ^ (w53 >> 07) +% w61 +% rot(w66, 19) ^ rot(w66, 61) ^ (w66 >> 06); - let w69 = w53 +% rot(w54, 01) ^ rot(w54, 08) ^ (w54 >> 07) +% w62 +% rot(w67, 19) ^ rot(w67, 61) ^ (w67 >> 06); - let w70 = w54 +% rot(w55, 01) ^ rot(w55, 08) ^ (w55 >> 07) +% w63 +% rot(w68, 19) ^ rot(w68, 61) ^ (w68 >> 06); - let w71 = w55 +% rot(w56, 01) ^ rot(w56, 08) ^ (w56 >> 07) +% w64 +% rot(w69, 19) ^ rot(w69, 61) ^ (w69 >> 06); - let w72 = w56 +% rot(w57, 01) ^ rot(w57, 08) ^ (w57 >> 07) +% w65 +% rot(w70, 19) ^ rot(w70, 61) ^ (w70 >> 06); - let w73 = w57 +% rot(w58, 01) ^ rot(w58, 08) ^ (w58 >> 07) +% w66 +% rot(w71, 19) ^ rot(w71, 61) ^ (w71 >> 06); - let w74 = w58 +% rot(w59, 01) ^ rot(w59, 08) ^ (w59 >> 07) +% w67 +% rot(w72, 19) ^ rot(w72, 61) ^ (w72 >> 06); - let w75 = w59 +% rot(w60, 01) ^ rot(w60, 08) ^ (w60 >> 07) +% w68 +% rot(w73, 19) ^ rot(w73, 61) ^ (w73 >> 06); - let w76 = w60 +% rot(w61, 01) ^ rot(w61, 08) ^ (w61 >> 07) +% w69 +% rot(w74, 19) ^ rot(w74, 61) ^ (w74 >> 06); - let w77 = w61 +% rot(w62, 01) ^ rot(w62, 08) ^ (w62 >> 07) +% w70 +% rot(w75, 19) ^ rot(w75, 61) ^ (w75 >> 06); - let w78 = w62 +% rot(w63, 01) ^ rot(w63, 08) ^ (w63 >> 07) +% w71 +% rot(w76, 19) ^ rot(w76, 61) ^ (w76 >> 06); - let w79 = w63 +% rot(w64, 01) ^ rot(w64, 08) ^ (w64 >> 07) +% w72 +% rot(w77, 19) ^ rot(w77, 61) ^ (w77 >> 06); - - // prettier-ignore - do { - t := h +% K.K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K09 +% w09 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K10 +% w10 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K11 +% w11 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K12 +% w12 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K13 +% w13 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K14 +% w14 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K64 +% w64 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K65 +% w65 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K66 +% w66 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K67 +% w67 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K68 +% w68 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K69 +% w69 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K70 +% w70 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K71 +% w71 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K72 +% w72 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K73 +% w73 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K74 +% w74 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K75 +% w75 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K76 +% w76 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K77 +% w77 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K78 +% w78 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K79 +% w79 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - }; - - // final addition - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - // counters - i += 128; - }; - // write state back to registers - state[0] := a; - state[1] := b; - state[2] := c; - state[3] := d; - state[4] := e; - state[5] := f; - state[6] := g; - state[7] := h; - - return i; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/whole_blocks/iter.mo b/.mops/sha2@0.2.5/src/sha512/whole_blocks/iter.mo deleted file mode 100644 index 1ed6f2d..0000000 --- a/.mops/sha2@0.2.5/src/sha512/whole_blocks/iter.mo +++ /dev/null @@ -1,373 +0,0 @@ -import VarArray "mo:core/VarArray"; -import Prim "mo:prim"; -import K "../constants"; -import Accessor "../write/accessor"; - -module { - - type Digest = { - // msg buffer - msg : [var Nat64]; - var word : Nat64; - var i_msg : Nat8; - var i_byte : Nat8; - var i_block : Nat64; - // state variables - s : [var Nat64]; - }; - - let nat32To64 = Prim.nat32ToNat64; - let nat16To32 = Prim.nat16ToNat32; - let nat8To16 = Prim.nat8ToNat16; - - func rot(x : Nat64, y : Nat64) : Nat64 = x <>> y; - - /// Consume bytes from the iterator `data` in 128-byte chunks, running the SHA512 compression on each full block. Stops when `data` returns `null`; any trailing partial block is left in `x.msg` for the writer to flush. - public func process_blocks(x : Digest, data : () -> ?Nat8) { - let state = x.s; - // load state registers - var a = state[0]; - var b = state[1]; - var c = state[2]; - var d = state[3]; - var e = state[4]; - var f = state[5]; - var g = state[6]; - var h = state[7]; - var t = 0 : Nat64; - - let backup = VarArray.repeat(0, 128); - var pos = 0; - ignore do ? { - // prettier-ignore - loop { - - let b000 = data()!; backup[0] := b000; pos := 1; - let b001 = data()!; backup[1] := b001; pos := 2; - let b002 = data()!; backup[2] := b002; pos := 3; - let b003 = data()!; backup[3] := b003; pos := 4; - let b004 = data()!; backup[4] := b004; pos := 5; - let b005 = data()!; backup[5] := b005; pos := 6; - let b006 = data()!; backup[6] := b006; pos := 7; - let b007 = data()!; backup[7] := b007; pos := 8; - let b008 = data()!; backup[8] := b008; pos := 9; - let b009 = data()!; backup[9] := b009; pos := 10; - let b010 = data()!; backup[10] := b010; pos := 11; - let b011 = data()!; backup[11] := b011; pos := 12; - let b012 = data()!; backup[12] := b012; pos := 13; - let b013 = data()!; backup[13] := b013; pos := 14; - let b014 = data()!; backup[14] := b014; pos := 15; - let b015 = data()!; backup[15] := b015; pos := 16; - let b016 = data()!; backup[16] := b016; pos := 17; - let b017 = data()!; backup[17] := b017; pos := 18; - let b018 = data()!; backup[18] := b018; pos := 19; - let b019 = data()!; backup[19] := b019; pos := 20; - let b020 = data()!; backup[20] := b020; pos := 21; - let b021 = data()!; backup[21] := b021; pos := 22; - let b022 = data()!; backup[22] := b022; pos := 23; - let b023 = data()!; backup[23] := b023; pos := 24; - let b024 = data()!; backup[24] := b024; pos := 25; - let b025 = data()!; backup[25] := b025; pos := 26; - let b026 = data()!; backup[26] := b026; pos := 27; - let b027 = data()!; backup[27] := b027; pos := 28; - let b028 = data()!; backup[28] := b028; pos := 29; - let b029 = data()!; backup[29] := b029; pos := 30; - let b030 = data()!; backup[30] := b030; pos := 31; - let b031 = data()!; backup[31] := b031; pos := 32; - let b032 = data()!; backup[32] := b032; pos := 33; - let b033 = data()!; backup[33] := b033; pos := 34; - let b034 = data()!; backup[34] := b034; pos := 35; - let b035 = data()!; backup[35] := b035; pos := 36; - let b036 = data()!; backup[36] := b036; pos := 37; - let b037 = data()!; backup[37] := b037; pos := 38; - let b038 = data()!; backup[38] := b038; pos := 39; - let b039 = data()!; backup[39] := b039; pos := 40; - let b040 = data()!; backup[40] := b040; pos := 41; - let b041 = data()!; backup[41] := b041; pos := 42; - let b042 = data()!; backup[42] := b042; pos := 43; - let b043 = data()!; backup[43] := b043; pos := 44; - let b044 = data()!; backup[44] := b044; pos := 45; - let b045 = data()!; backup[45] := b045; pos := 46; - let b046 = data()!; backup[46] := b046; pos := 47; - let b047 = data()!; backup[47] := b047; pos := 48; - let b048 = data()!; backup[48] := b048; pos := 49; - let b049 = data()!; backup[49] := b049; pos := 50; - let b050 = data()!; backup[50] := b050; pos := 51; - let b051 = data()!; backup[51] := b051; pos := 52; - let b052 = data()!; backup[52] := b052; pos := 53; - let b053 = data()!; backup[53] := b053; pos := 54; - let b054 = data()!; backup[54] := b054; pos := 55; - let b055 = data()!; backup[55] := b055; pos := 56; - let b056 = data()!; backup[56] := b056; pos := 57; - let b057 = data()!; backup[57] := b057; pos := 58; - let b058 = data()!; backup[58] := b058; pos := 59; - let b059 = data()!; backup[59] := b059; pos := 60; - let b060 = data()!; backup[60] := b060; pos := 61; - let b061 = data()!; backup[61] := b061; pos := 62; - let b062 = data()!; backup[62] := b062; pos := 63; - let b063 = data()!; backup[63] := b063; pos := 64; - let b064 = data()!; backup[64] := b064; pos := 65; - let b065 = data()!; backup[65] := b065; pos := 66; - let b066 = data()!; backup[66] := b066; pos := 67; - let b067 = data()!; backup[67] := b067; pos := 68; - let b068 = data()!; backup[68] := b068; pos := 69; - let b069 = data()!; backup[69] := b069; pos := 70; - let b070 = data()!; backup[70] := b070; pos := 71; - let b071 = data()!; backup[71] := b071; pos := 72; - let b072 = data()!; backup[72] := b072; pos := 73; - let b073 = data()!; backup[73] := b073; pos := 74; - let b074 = data()!; backup[74] := b074; pos := 75; - let b075 = data()!; backup[75] := b075; pos := 76; - let b076 = data()!; backup[76] := b076; pos := 77; - let b077 = data()!; backup[77] := b077; pos := 78; - let b078 = data()!; backup[78] := b078; pos := 79; - let b079 = data()!; backup[79] := b079; pos := 80; - let b080 = data()!; backup[80] := b080; pos := 81; - let b081 = data()!; backup[81] := b081; pos := 82; - let b082 = data()!; backup[82] := b082; pos := 83; - let b083 = data()!; backup[83] := b083; pos := 84; - let b084 = data()!; backup[84] := b084; pos := 85; - let b085 = data()!; backup[85] := b085; pos := 86; - let b086 = data()!; backup[86] := b086; pos := 87; - let b087 = data()!; backup[87] := b087; pos := 88; - let b088 = data()!; backup[88] := b088; pos := 89; - let b089 = data()!; backup[89] := b089; pos := 90; - let b090 = data()!; backup[90] := b090; pos := 91; - let b091 = data()!; backup[91] := b091; pos := 92; - let b092 = data()!; backup[92] := b092; pos := 93; - let b093 = data()!; backup[93] := b093; pos := 94; - let b094 = data()!; backup[94] := b094; pos := 95; - let b095 = data()!; backup[95] := b095; pos := 96; - let b096 = data()!; backup[96] := b096; pos := 97; - let b097 = data()!; backup[97] := b097; pos := 98; - let b098 = data()!; backup[98] := b098; pos := 99; - let b099 = data()!; backup[99] := b099; pos := 100; - let b100 = data()!; backup[100] := b100; pos := 101; - let b101 = data()!; backup[101] := b101; pos := 102; - let b102 = data()!; backup[102] := b102; pos := 103; - let b103 = data()!; backup[103] := b103; pos := 104; - let b104 = data()!; backup[104] := b104; pos := 105; - let b105 = data()!; backup[105] := b105; pos := 106; - let b106 = data()!; backup[106] := b106; pos := 107; - let b107 = data()!; backup[107] := b107; pos := 108; - let b108 = data()!; backup[108] := b108; pos := 109; - let b109 = data()!; backup[109] := b109; pos := 110; - let b110 = data()!; backup[110] := b110; pos := 111; - let b111 = data()!; backup[111] := b111; pos := 112; - let b112 = data()!; backup[112] := b112; pos := 113; - let b113 = data()!; backup[113] := b113; pos := 114; - let b114 = data()!; backup[114] := b114; pos := 115; - let b115 = data()!; backup[115] := b115; pos := 116; - let b116 = data()!; backup[116] := b116; pos := 117; - let b117 = data()!; backup[117] := b117; pos := 118; - let b118 = data()!; backup[118] := b118; pos := 119; - let b119 = data()!; backup[119] := b119; pos := 120; - let b120 = data()!; backup[120] := b120; pos := 121; - let b121 = data()!; backup[121] := b121; pos := 122; - let b122 = data()!; backup[122] := b122; pos := 123; - let b123 = data()!; backup[123] := b123; pos := 124; - let b124 = data()!; backup[124] := b124; pos := 125; - let b125 = data()!; backup[125] := b125; pos := 126; - let b126 = data()!; backup[126] := b126; pos := 127; - let b127 = data()!; backup[127] := b127; pos := 0; - - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - - let w00 = nat32To64(nat16To32(nat8To16(b000))) << 56 | nat32To64(nat16To32(nat8To16(b001))) << 48 | nat32To64(nat16To32(nat8To16(b002))) << 40 | nat32To64(nat16To32(nat8To16(b003))) << 32 | nat32To64(nat16To32(nat8To16(b004))) << 24 | nat32To64(nat16To32(nat8To16(b005))) << 16 | nat32To64(nat16To32(nat8To16(b006))) << 8 | nat32To64(nat16To32(nat8To16(b007))); - let w01 = nat32To64(nat16To32(nat8To16(b008))) << 56 | nat32To64(nat16To32(nat8To16(b009))) << 48 | nat32To64(nat16To32(nat8To16(b010))) << 40 | nat32To64(nat16To32(nat8To16(b011))) << 32 | nat32To64(nat16To32(nat8To16(b012))) << 24 | nat32To64(nat16To32(nat8To16(b013))) << 16 | nat32To64(nat16To32(nat8To16(b014))) << 8 | nat32To64(nat16To32(nat8To16(b015))); - let w02 = nat32To64(nat16To32(nat8To16(b016))) << 56 | nat32To64(nat16To32(nat8To16(b017))) << 48 | nat32To64(nat16To32(nat8To16(b018))) << 40 | nat32To64(nat16To32(nat8To16(b019))) << 32 | nat32To64(nat16To32(nat8To16(b020))) << 24 | nat32To64(nat16To32(nat8To16(b021))) << 16 | nat32To64(nat16To32(nat8To16(b022))) << 8 | nat32To64(nat16To32(nat8To16(b023))); - let w03 = nat32To64(nat16To32(nat8To16(b024))) << 56 | nat32To64(nat16To32(nat8To16(b025))) << 48 | nat32To64(nat16To32(nat8To16(b026))) << 40 | nat32To64(nat16To32(nat8To16(b027))) << 32 | nat32To64(nat16To32(nat8To16(b028))) << 24 | nat32To64(nat16To32(nat8To16(b029))) << 16 | nat32To64(nat16To32(nat8To16(b030))) << 8 | nat32To64(nat16To32(nat8To16(b031))); - let w04 = nat32To64(nat16To32(nat8To16(b032))) << 56 | nat32To64(nat16To32(nat8To16(b033))) << 48 | nat32To64(nat16To32(nat8To16(b034))) << 40 | nat32To64(nat16To32(nat8To16(b035))) << 32 | nat32To64(nat16To32(nat8To16(b036))) << 24 | nat32To64(nat16To32(nat8To16(b037))) << 16 | nat32To64(nat16To32(nat8To16(b038))) << 8 | nat32To64(nat16To32(nat8To16(b039))); - let w05 = nat32To64(nat16To32(nat8To16(b040))) << 56 | nat32To64(nat16To32(nat8To16(b041))) << 48 | nat32To64(nat16To32(nat8To16(b042))) << 40 | nat32To64(nat16To32(nat8To16(b043))) << 32 | nat32To64(nat16To32(nat8To16(b044))) << 24 | nat32To64(nat16To32(nat8To16(b045))) << 16 | nat32To64(nat16To32(nat8To16(b046))) << 8 | nat32To64(nat16To32(nat8To16(b047))); - let w06 = nat32To64(nat16To32(nat8To16(b048))) << 56 | nat32To64(nat16To32(nat8To16(b049))) << 48 | nat32To64(nat16To32(nat8To16(b050))) << 40 | nat32To64(nat16To32(nat8To16(b051))) << 32 | nat32To64(nat16To32(nat8To16(b052))) << 24 | nat32To64(nat16To32(nat8To16(b053))) << 16 | nat32To64(nat16To32(nat8To16(b054))) << 8 | nat32To64(nat16To32(nat8To16(b055))); - let w07 = nat32To64(nat16To32(nat8To16(b056))) << 56 | nat32To64(nat16To32(nat8To16(b057))) << 48 | nat32To64(nat16To32(nat8To16(b058))) << 40 | nat32To64(nat16To32(nat8To16(b059))) << 32 | nat32To64(nat16To32(nat8To16(b060))) << 24 | nat32To64(nat16To32(nat8To16(b061))) << 16 | nat32To64(nat16To32(nat8To16(b062))) << 8 | nat32To64(nat16To32(nat8To16(b063))); - let w08 = nat32To64(nat16To32(nat8To16(b064))) << 56 | nat32To64(nat16To32(nat8To16(b065))) << 48 | nat32To64(nat16To32(nat8To16(b066))) << 40 | nat32To64(nat16To32(nat8To16(b067))) << 32 | nat32To64(nat16To32(nat8To16(b068))) << 24 | nat32To64(nat16To32(nat8To16(b069))) << 16 | nat32To64(nat16To32(nat8To16(b070))) << 8 | nat32To64(nat16To32(nat8To16(b071))); - let w09 = nat32To64(nat16To32(nat8To16(b072))) << 56 | nat32To64(nat16To32(nat8To16(b073))) << 48 | nat32To64(nat16To32(nat8To16(b074))) << 40 | nat32To64(nat16To32(nat8To16(b075))) << 32 | nat32To64(nat16To32(nat8To16(b076))) << 24 | nat32To64(nat16To32(nat8To16(b077))) << 16 | nat32To64(nat16To32(nat8To16(b078))) << 8 | nat32To64(nat16To32(nat8To16(b079))); - let w10 = nat32To64(nat16To32(nat8To16(b080))) << 56 | nat32To64(nat16To32(nat8To16(b081))) << 48 | nat32To64(nat16To32(nat8To16(b082))) << 40 | nat32To64(nat16To32(nat8To16(b083))) << 32 | nat32To64(nat16To32(nat8To16(b084))) << 24 | nat32To64(nat16To32(nat8To16(b085))) << 16 | nat32To64(nat16To32(nat8To16(b086))) << 8 | nat32To64(nat16To32(nat8To16(b087))); - let w11 = nat32To64(nat16To32(nat8To16(b088))) << 56 | nat32To64(nat16To32(nat8To16(b089))) << 48 | nat32To64(nat16To32(nat8To16(b090))) << 40 | nat32To64(nat16To32(nat8To16(b091))) << 32 | nat32To64(nat16To32(nat8To16(b092))) << 24 | nat32To64(nat16To32(nat8To16(b093))) << 16 | nat32To64(nat16To32(nat8To16(b094))) << 8 | nat32To64(nat16To32(nat8To16(b095))); - let w12 = nat32To64(nat16To32(nat8To16(b096))) << 56 | nat32To64(nat16To32(nat8To16(b097))) << 48 | nat32To64(nat16To32(nat8To16(b098))) << 40 | nat32To64(nat16To32(nat8To16(b099))) << 32 | nat32To64(nat16To32(nat8To16(b100))) << 24 | nat32To64(nat16To32(nat8To16(b101))) << 16 | nat32To64(nat16To32(nat8To16(b102))) << 8 | nat32To64(nat16To32(nat8To16(b103))); - let w13 = nat32To64(nat16To32(nat8To16(b104))) << 56 | nat32To64(nat16To32(nat8To16(b105))) << 48 | nat32To64(nat16To32(nat8To16(b106))) << 40 | nat32To64(nat16To32(nat8To16(b107))) << 32 | nat32To64(nat16To32(nat8To16(b108))) << 24 | nat32To64(nat16To32(nat8To16(b109))) << 16 | nat32To64(nat16To32(nat8To16(b110))) << 8 | nat32To64(nat16To32(nat8To16(b111))); - let w14 = nat32To64(nat16To32(nat8To16(b112))) << 56 | nat32To64(nat16To32(nat8To16(b113))) << 48 | nat32To64(nat16To32(nat8To16(b114))) << 40 | nat32To64(nat16To32(nat8To16(b115))) << 32 | nat32To64(nat16To32(nat8To16(b116))) << 24 | nat32To64(nat16To32(nat8To16(b117))) << 16 | nat32To64(nat16To32(nat8To16(b118))) << 8 | nat32To64(nat16To32(nat8To16(b119))); - let w15 = nat32To64(nat16To32(nat8To16(b120))) << 56 | nat32To64(nat16To32(nat8To16(b121))) << 48 | nat32To64(nat16To32(nat8To16(b122))) << 40 | nat32To64(nat16To32(nat8To16(b123))) << 32 | nat32To64(nat16To32(nat8To16(b124))) << 24 | nat32To64(nat16To32(nat8To16(b125))) << 16 | nat32To64(nat16To32(nat8To16(b126))) << 8 | nat32To64(nat16To32(nat8To16(b127))); - - let w16 = w00 +% rot(w01, 01) ^ rot(w01, 08) ^ (w01 >> 07) +% w09 +% rot(w14, 19) ^ rot(w14, 61) ^ (w14 >> 06); - let w17 = w01 +% rot(w02, 01) ^ rot(w02, 08) ^ (w02 >> 07) +% w10 +% rot(w15, 19) ^ rot(w15, 61) ^ (w15 >> 06); - let w18 = w02 +% rot(w03, 01) ^ rot(w03, 08) ^ (w03 >> 07) +% w11 +% rot(w16, 19) ^ rot(w16, 61) ^ (w16 >> 06); - let w19 = w03 +% rot(w04, 01) ^ rot(w04, 08) ^ (w04 >> 07) +% w12 +% rot(w17, 19) ^ rot(w17, 61) ^ (w17 >> 06); - let w20 = w04 +% rot(w05, 01) ^ rot(w05, 08) ^ (w05 >> 07) +% w13 +% rot(w18, 19) ^ rot(w18, 61) ^ (w18 >> 06); - let w21 = w05 +% rot(w06, 01) ^ rot(w06, 08) ^ (w06 >> 07) +% w14 +% rot(w19, 19) ^ rot(w19, 61) ^ (w19 >> 06); - let w22 = w06 +% rot(w07, 01) ^ rot(w07, 08) ^ (w07 >> 07) +% w15 +% rot(w20, 19) ^ rot(w20, 61) ^ (w20 >> 06); - let w23 = w07 +% rot(w08, 01) ^ rot(w08, 08) ^ (w08 >> 07) +% w16 +% rot(w21, 19) ^ rot(w21, 61) ^ (w21 >> 06); - let w24 = w08 +% rot(w09, 01) ^ rot(w09, 08) ^ (w09 >> 07) +% w17 +% rot(w22, 19) ^ rot(w22, 61) ^ (w22 >> 06); - let w25 = w09 +% rot(w10, 01) ^ rot(w10, 08) ^ (w10 >> 07) +% w18 +% rot(w23, 19) ^ rot(w23, 61) ^ (w23 >> 06); - let w26 = w10 +% rot(w11, 01) ^ rot(w11, 08) ^ (w11 >> 07) +% w19 +% rot(w24, 19) ^ rot(w24, 61) ^ (w24 >> 06); - let w27 = w11 +% rot(w12, 01) ^ rot(w12, 08) ^ (w12 >> 07) +% w20 +% rot(w25, 19) ^ rot(w25, 61) ^ (w25 >> 06); - let w28 = w12 +% rot(w13, 01) ^ rot(w13, 08) ^ (w13 >> 07) +% w21 +% rot(w26, 19) ^ rot(w26, 61) ^ (w26 >> 06); - let w29 = w13 +% rot(w14, 01) ^ rot(w14, 08) ^ (w14 >> 07) +% w22 +% rot(w27, 19) ^ rot(w27, 61) ^ (w27 >> 06); - let w30 = w14 +% rot(w15, 01) ^ rot(w15, 08) ^ (w15 >> 07) +% w23 +% rot(w28, 19) ^ rot(w28, 61) ^ (w28 >> 06); - let w31 = w15 +% rot(w16, 01) ^ rot(w16, 08) ^ (w16 >> 07) +% w24 +% rot(w29, 19) ^ rot(w29, 61) ^ (w29 >> 06); - let w32 = w16 +% rot(w17, 01) ^ rot(w17, 08) ^ (w17 >> 07) +% w25 +% rot(w30, 19) ^ rot(w30, 61) ^ (w30 >> 06); - let w33 = w17 +% rot(w18, 01) ^ rot(w18, 08) ^ (w18 >> 07) +% w26 +% rot(w31, 19) ^ rot(w31, 61) ^ (w31 >> 06); - let w34 = w18 +% rot(w19, 01) ^ rot(w19, 08) ^ (w19 >> 07) +% w27 +% rot(w32, 19) ^ rot(w32, 61) ^ (w32 >> 06); - let w35 = w19 +% rot(w20, 01) ^ rot(w20, 08) ^ (w20 >> 07) +% w28 +% rot(w33, 19) ^ rot(w33, 61) ^ (w33 >> 06); - let w36 = w20 +% rot(w21, 01) ^ rot(w21, 08) ^ (w21 >> 07) +% w29 +% rot(w34, 19) ^ rot(w34, 61) ^ (w34 >> 06); - let w37 = w21 +% rot(w22, 01) ^ rot(w22, 08) ^ (w22 >> 07) +% w30 +% rot(w35, 19) ^ rot(w35, 61) ^ (w35 >> 06); - let w38 = w22 +% rot(w23, 01) ^ rot(w23, 08) ^ (w23 >> 07) +% w31 +% rot(w36, 19) ^ rot(w36, 61) ^ (w36 >> 06); - let w39 = w23 +% rot(w24, 01) ^ rot(w24, 08) ^ (w24 >> 07) +% w32 +% rot(w37, 19) ^ rot(w37, 61) ^ (w37 >> 06); - let w40 = w24 +% rot(w25, 01) ^ rot(w25, 08) ^ (w25 >> 07) +% w33 +% rot(w38, 19) ^ rot(w38, 61) ^ (w38 >> 06); - let w41 = w25 +% rot(w26, 01) ^ rot(w26, 08) ^ (w26 >> 07) +% w34 +% rot(w39, 19) ^ rot(w39, 61) ^ (w39 >> 06); - let w42 = w26 +% rot(w27, 01) ^ rot(w27, 08) ^ (w27 >> 07) +% w35 +% rot(w40, 19) ^ rot(w40, 61) ^ (w40 >> 06); - let w43 = w27 +% rot(w28, 01) ^ rot(w28, 08) ^ (w28 >> 07) +% w36 +% rot(w41, 19) ^ rot(w41, 61) ^ (w41 >> 06); - let w44 = w28 +% rot(w29, 01) ^ rot(w29, 08) ^ (w29 >> 07) +% w37 +% rot(w42, 19) ^ rot(w42, 61) ^ (w42 >> 06); - let w45 = w29 +% rot(w30, 01) ^ rot(w30, 08) ^ (w30 >> 07) +% w38 +% rot(w43, 19) ^ rot(w43, 61) ^ (w43 >> 06); - let w46 = w30 +% rot(w31, 01) ^ rot(w31, 08) ^ (w31 >> 07) +% w39 +% rot(w44, 19) ^ rot(w44, 61) ^ (w44 >> 06); - let w47 = w31 +% rot(w32, 01) ^ rot(w32, 08) ^ (w32 >> 07) +% w40 +% rot(w45, 19) ^ rot(w45, 61) ^ (w45 >> 06); - let w48 = w32 +% rot(w33, 01) ^ rot(w33, 08) ^ (w33 >> 07) +% w41 +% rot(w46, 19) ^ rot(w46, 61) ^ (w46 >> 06); - let w49 = w33 +% rot(w34, 01) ^ rot(w34, 08) ^ (w34 >> 07) +% w42 +% rot(w47, 19) ^ rot(w47, 61) ^ (w47 >> 06); - let w50 = w34 +% rot(w35, 01) ^ rot(w35, 08) ^ (w35 >> 07) +% w43 +% rot(w48, 19) ^ rot(w48, 61) ^ (w48 >> 06); - let w51 = w35 +% rot(w36, 01) ^ rot(w36, 08) ^ (w36 >> 07) +% w44 +% rot(w49, 19) ^ rot(w49, 61) ^ (w49 >> 06); - let w52 = w36 +% rot(w37, 01) ^ rot(w37, 08) ^ (w37 >> 07) +% w45 +% rot(w50, 19) ^ rot(w50, 61) ^ (w50 >> 06); - let w53 = w37 +% rot(w38, 01) ^ rot(w38, 08) ^ (w38 >> 07) +% w46 +% rot(w51, 19) ^ rot(w51, 61) ^ (w51 >> 06); - let w54 = w38 +% rot(w39, 01) ^ rot(w39, 08) ^ (w39 >> 07) +% w47 +% rot(w52, 19) ^ rot(w52, 61) ^ (w52 >> 06); - let w55 = w39 +% rot(w40, 01) ^ rot(w40, 08) ^ (w40 >> 07) +% w48 +% rot(w53, 19) ^ rot(w53, 61) ^ (w53 >> 06); - let w56 = w40 +% rot(w41, 01) ^ rot(w41, 08) ^ (w41 >> 07) +% w49 +% rot(w54, 19) ^ rot(w54, 61) ^ (w54 >> 06); - let w57 = w41 +% rot(w42, 01) ^ rot(w42, 08) ^ (w42 >> 07) +% w50 +% rot(w55, 19) ^ rot(w55, 61) ^ (w55 >> 06); - let w58 = w42 +% rot(w43, 01) ^ rot(w43, 08) ^ (w43 >> 07) +% w51 +% rot(w56, 19) ^ rot(w56, 61) ^ (w56 >> 06); - let w59 = w43 +% rot(w44, 01) ^ rot(w44, 08) ^ (w44 >> 07) +% w52 +% rot(w57, 19) ^ rot(w57, 61) ^ (w57 >> 06); - let w60 = w44 +% rot(w45, 01) ^ rot(w45, 08) ^ (w45 >> 07) +% w53 +% rot(w58, 19) ^ rot(w58, 61) ^ (w58 >> 06); - let w61 = w45 +% rot(w46, 01) ^ rot(w46, 08) ^ (w46 >> 07) +% w54 +% rot(w59, 19) ^ rot(w59, 61) ^ (w59 >> 06); - let w62 = w46 +% rot(w47, 01) ^ rot(w47, 08) ^ (w47 >> 07) +% w55 +% rot(w60, 19) ^ rot(w60, 61) ^ (w60 >> 06); - let w63 = w47 +% rot(w48, 01) ^ rot(w48, 08) ^ (w48 >> 07) +% w56 +% rot(w61, 19) ^ rot(w61, 61) ^ (w61 >> 06); - let w64 = w48 +% rot(w49, 01) ^ rot(w49, 08) ^ (w49 >> 07) +% w57 +% rot(w62, 19) ^ rot(w62, 61) ^ (w62 >> 06); - let w65 = w49 +% rot(w50, 01) ^ rot(w50, 08) ^ (w50 >> 07) +% w58 +% rot(w63, 19) ^ rot(w63, 61) ^ (w63 >> 06); - let w66 = w50 +% rot(w51, 01) ^ rot(w51, 08) ^ (w51 >> 07) +% w59 +% rot(w64, 19) ^ rot(w64, 61) ^ (w64 >> 06); - let w67 = w51 +% rot(w52, 01) ^ rot(w52, 08) ^ (w52 >> 07) +% w60 +% rot(w65, 19) ^ rot(w65, 61) ^ (w65 >> 06); - let w68 = w52 +% rot(w53, 01) ^ rot(w53, 08) ^ (w53 >> 07) +% w61 +% rot(w66, 19) ^ rot(w66, 61) ^ (w66 >> 06); - let w69 = w53 +% rot(w54, 01) ^ rot(w54, 08) ^ (w54 >> 07) +% w62 +% rot(w67, 19) ^ rot(w67, 61) ^ (w67 >> 06); - let w70 = w54 +% rot(w55, 01) ^ rot(w55, 08) ^ (w55 >> 07) +% w63 +% rot(w68, 19) ^ rot(w68, 61) ^ (w68 >> 06); - let w71 = w55 +% rot(w56, 01) ^ rot(w56, 08) ^ (w56 >> 07) +% w64 +% rot(w69, 19) ^ rot(w69, 61) ^ (w69 >> 06); - let w72 = w56 +% rot(w57, 01) ^ rot(w57, 08) ^ (w57 >> 07) +% w65 +% rot(w70, 19) ^ rot(w70, 61) ^ (w70 >> 06); - let w73 = w57 +% rot(w58, 01) ^ rot(w58, 08) ^ (w58 >> 07) +% w66 +% rot(w71, 19) ^ rot(w71, 61) ^ (w71 >> 06); - let w74 = w58 +% rot(w59, 01) ^ rot(w59, 08) ^ (w59 >> 07) +% w67 +% rot(w72, 19) ^ rot(w72, 61) ^ (w72 >> 06); - let w75 = w59 +% rot(w60, 01) ^ rot(w60, 08) ^ (w60 >> 07) +% w68 +% rot(w73, 19) ^ rot(w73, 61) ^ (w73 >> 06); - let w76 = w60 +% rot(w61, 01) ^ rot(w61, 08) ^ (w61 >> 07) +% w69 +% rot(w74, 19) ^ rot(w74, 61) ^ (w74 >> 06); - let w77 = w61 +% rot(w62, 01) ^ rot(w62, 08) ^ (w62 >> 07) +% w70 +% rot(w75, 19) ^ rot(w75, 61) ^ (w75 >> 06); - let w78 = w62 +% rot(w63, 01) ^ rot(w63, 08) ^ (w63 >> 07) +% w71 +% rot(w76, 19) ^ rot(w76, 61) ^ (w76 >> 06); - let w79 = w63 +% rot(w64, 01) ^ rot(w64, 08) ^ (w64 >> 07) +% w72 +% rot(w77, 19) ^ rot(w77, 61) ^ (w77 >> 06); - - t := h +% K.K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K09 +% w09 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K10 +% w10 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K11 +% w11 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K12 +% w12 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K13 +% w13 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K14 +% w14 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K64 +% w64 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K65 +% w65 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K66 +% w66 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K67 +% w67 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K68 +% w68 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K69 +% w69 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K70 +% w70 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K71 +% w71 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K72 +% w72 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K73 +% w73 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K74 +% w74 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K75 +% w75 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K76 +% w76 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K77 +% w77 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K78 +% w78 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K79 +% w79 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - - // final addition - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - // counters - x.i_block +%= 1; - }; - }; - // write state back to registers - state[0] := a; - state[1] := b; - state[2] := c; - state[3] := d; - state[4] := e; - state[5] := f; - state[6] := g; - state[7] := h; - - // write remaining bytes from backup to buffer - Accessor.write(x, func(i) = backup[i], 0, pos); - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/whole_blocks/reader.mo b/.mops/sha2@0.2.5/src/sha512/whole_blocks/reader.mo deleted file mode 100644 index 8b431d4..0000000 --- a/.mops/sha2@0.2.5/src/sha512/whole_blocks/reader.mo +++ /dev/null @@ -1,226 +0,0 @@ -import Prim "mo:prim"; -import K "../constants"; - -module { - func rot(x : Nat64, y : Nat64) : Nat64 = x <>> y; - - let nat32To64 = Prim.nat32ToNat64; - let nat16To32 = Prim.nat16ToNat32; - let nat8To16 = Prim.nat8ToNat16; - - /// Run the SHA512 compression on every full 128-byte block read via repeated calls to `data`. Treats `start` as the byte-position counter and stops once `start + bytes_consumed` would exceed `sz`. Returns the index just past the last block consumed (i.e. `start + 128 * blocks`). - public func process_blocks(state : [var Nat64], data : () -> Nat8, sz : Nat, start : Nat) : Nat { - var i = start; - // load state registers - var a = state[0]; - var b = state[1]; - var c = state[2]; - var d = state[3]; - var e = state[4]; - var f = state[5]; - var g = state[6]; - var h = state[7]; - var t = 0 : Nat64; - var i_max : Nat = i + ((sz - i) / 128) * 128; - while (i < i_max) { - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - - let w00 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w01 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w02 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w03 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w04 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w05 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w06 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w07 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w08 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w09 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w10 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w11 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w12 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w13 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w14 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - let w15 = nat32To64(nat16To32(nat8To16(data()))) << 56 | nat32To64(nat16To32(nat8To16(data()))) << 48 | nat32To64(nat16To32(nat8To16(data()))) << 40 | nat32To64(nat16To32(nat8To16(data()))) << 32 | nat32To64(nat16To32(nat8To16(data()))) << 24 | nat32To64(nat16To32(nat8To16(data()))) << 16 | nat32To64(nat16To32(nat8To16(data()))) << 8 | nat32To64(nat16To32(nat8To16(data()))); - - let w16 = w00 +% rot(w01, 01) ^ rot(w01, 08) ^ (w01 >> 07) +% w09 +% rot(w14, 19) ^ rot(w14, 61) ^ (w14 >> 06); - let w17 = w01 +% rot(w02, 01) ^ rot(w02, 08) ^ (w02 >> 07) +% w10 +% rot(w15, 19) ^ rot(w15, 61) ^ (w15 >> 06); - let w18 = w02 +% rot(w03, 01) ^ rot(w03, 08) ^ (w03 >> 07) +% w11 +% rot(w16, 19) ^ rot(w16, 61) ^ (w16 >> 06); - let w19 = w03 +% rot(w04, 01) ^ rot(w04, 08) ^ (w04 >> 07) +% w12 +% rot(w17, 19) ^ rot(w17, 61) ^ (w17 >> 06); - let w20 = w04 +% rot(w05, 01) ^ rot(w05, 08) ^ (w05 >> 07) +% w13 +% rot(w18, 19) ^ rot(w18, 61) ^ (w18 >> 06); - let w21 = w05 +% rot(w06, 01) ^ rot(w06, 08) ^ (w06 >> 07) +% w14 +% rot(w19, 19) ^ rot(w19, 61) ^ (w19 >> 06); - let w22 = w06 +% rot(w07, 01) ^ rot(w07, 08) ^ (w07 >> 07) +% w15 +% rot(w20, 19) ^ rot(w20, 61) ^ (w20 >> 06); - let w23 = w07 +% rot(w08, 01) ^ rot(w08, 08) ^ (w08 >> 07) +% w16 +% rot(w21, 19) ^ rot(w21, 61) ^ (w21 >> 06); - let w24 = w08 +% rot(w09, 01) ^ rot(w09, 08) ^ (w09 >> 07) +% w17 +% rot(w22, 19) ^ rot(w22, 61) ^ (w22 >> 06); - let w25 = w09 +% rot(w10, 01) ^ rot(w10, 08) ^ (w10 >> 07) +% w18 +% rot(w23, 19) ^ rot(w23, 61) ^ (w23 >> 06); - let w26 = w10 +% rot(w11, 01) ^ rot(w11, 08) ^ (w11 >> 07) +% w19 +% rot(w24, 19) ^ rot(w24, 61) ^ (w24 >> 06); - let w27 = w11 +% rot(w12, 01) ^ rot(w12, 08) ^ (w12 >> 07) +% w20 +% rot(w25, 19) ^ rot(w25, 61) ^ (w25 >> 06); - let w28 = w12 +% rot(w13, 01) ^ rot(w13, 08) ^ (w13 >> 07) +% w21 +% rot(w26, 19) ^ rot(w26, 61) ^ (w26 >> 06); - let w29 = w13 +% rot(w14, 01) ^ rot(w14, 08) ^ (w14 >> 07) +% w22 +% rot(w27, 19) ^ rot(w27, 61) ^ (w27 >> 06); - let w30 = w14 +% rot(w15, 01) ^ rot(w15, 08) ^ (w15 >> 07) +% w23 +% rot(w28, 19) ^ rot(w28, 61) ^ (w28 >> 06); - let w31 = w15 +% rot(w16, 01) ^ rot(w16, 08) ^ (w16 >> 07) +% w24 +% rot(w29, 19) ^ rot(w29, 61) ^ (w29 >> 06); - let w32 = w16 +% rot(w17, 01) ^ rot(w17, 08) ^ (w17 >> 07) +% w25 +% rot(w30, 19) ^ rot(w30, 61) ^ (w30 >> 06); - let w33 = w17 +% rot(w18, 01) ^ rot(w18, 08) ^ (w18 >> 07) +% w26 +% rot(w31, 19) ^ rot(w31, 61) ^ (w31 >> 06); - let w34 = w18 +% rot(w19, 01) ^ rot(w19, 08) ^ (w19 >> 07) +% w27 +% rot(w32, 19) ^ rot(w32, 61) ^ (w32 >> 06); - let w35 = w19 +% rot(w20, 01) ^ rot(w20, 08) ^ (w20 >> 07) +% w28 +% rot(w33, 19) ^ rot(w33, 61) ^ (w33 >> 06); - let w36 = w20 +% rot(w21, 01) ^ rot(w21, 08) ^ (w21 >> 07) +% w29 +% rot(w34, 19) ^ rot(w34, 61) ^ (w34 >> 06); - let w37 = w21 +% rot(w22, 01) ^ rot(w22, 08) ^ (w22 >> 07) +% w30 +% rot(w35, 19) ^ rot(w35, 61) ^ (w35 >> 06); - let w38 = w22 +% rot(w23, 01) ^ rot(w23, 08) ^ (w23 >> 07) +% w31 +% rot(w36, 19) ^ rot(w36, 61) ^ (w36 >> 06); - let w39 = w23 +% rot(w24, 01) ^ rot(w24, 08) ^ (w24 >> 07) +% w32 +% rot(w37, 19) ^ rot(w37, 61) ^ (w37 >> 06); - let w40 = w24 +% rot(w25, 01) ^ rot(w25, 08) ^ (w25 >> 07) +% w33 +% rot(w38, 19) ^ rot(w38, 61) ^ (w38 >> 06); - let w41 = w25 +% rot(w26, 01) ^ rot(w26, 08) ^ (w26 >> 07) +% w34 +% rot(w39, 19) ^ rot(w39, 61) ^ (w39 >> 06); - let w42 = w26 +% rot(w27, 01) ^ rot(w27, 08) ^ (w27 >> 07) +% w35 +% rot(w40, 19) ^ rot(w40, 61) ^ (w40 >> 06); - let w43 = w27 +% rot(w28, 01) ^ rot(w28, 08) ^ (w28 >> 07) +% w36 +% rot(w41, 19) ^ rot(w41, 61) ^ (w41 >> 06); - let w44 = w28 +% rot(w29, 01) ^ rot(w29, 08) ^ (w29 >> 07) +% w37 +% rot(w42, 19) ^ rot(w42, 61) ^ (w42 >> 06); - let w45 = w29 +% rot(w30, 01) ^ rot(w30, 08) ^ (w30 >> 07) +% w38 +% rot(w43, 19) ^ rot(w43, 61) ^ (w43 >> 06); - let w46 = w30 +% rot(w31, 01) ^ rot(w31, 08) ^ (w31 >> 07) +% w39 +% rot(w44, 19) ^ rot(w44, 61) ^ (w44 >> 06); - let w47 = w31 +% rot(w32, 01) ^ rot(w32, 08) ^ (w32 >> 07) +% w40 +% rot(w45, 19) ^ rot(w45, 61) ^ (w45 >> 06); - let w48 = w32 +% rot(w33, 01) ^ rot(w33, 08) ^ (w33 >> 07) +% w41 +% rot(w46, 19) ^ rot(w46, 61) ^ (w46 >> 06); - let w49 = w33 +% rot(w34, 01) ^ rot(w34, 08) ^ (w34 >> 07) +% w42 +% rot(w47, 19) ^ rot(w47, 61) ^ (w47 >> 06); - let w50 = w34 +% rot(w35, 01) ^ rot(w35, 08) ^ (w35 >> 07) +% w43 +% rot(w48, 19) ^ rot(w48, 61) ^ (w48 >> 06); - let w51 = w35 +% rot(w36, 01) ^ rot(w36, 08) ^ (w36 >> 07) +% w44 +% rot(w49, 19) ^ rot(w49, 61) ^ (w49 >> 06); - let w52 = w36 +% rot(w37, 01) ^ rot(w37, 08) ^ (w37 >> 07) +% w45 +% rot(w50, 19) ^ rot(w50, 61) ^ (w50 >> 06); - let w53 = w37 +% rot(w38, 01) ^ rot(w38, 08) ^ (w38 >> 07) +% w46 +% rot(w51, 19) ^ rot(w51, 61) ^ (w51 >> 06); - let w54 = w38 +% rot(w39, 01) ^ rot(w39, 08) ^ (w39 >> 07) +% w47 +% rot(w52, 19) ^ rot(w52, 61) ^ (w52 >> 06); - let w55 = w39 +% rot(w40, 01) ^ rot(w40, 08) ^ (w40 >> 07) +% w48 +% rot(w53, 19) ^ rot(w53, 61) ^ (w53 >> 06); - let w56 = w40 +% rot(w41, 01) ^ rot(w41, 08) ^ (w41 >> 07) +% w49 +% rot(w54, 19) ^ rot(w54, 61) ^ (w54 >> 06); - let w57 = w41 +% rot(w42, 01) ^ rot(w42, 08) ^ (w42 >> 07) +% w50 +% rot(w55, 19) ^ rot(w55, 61) ^ (w55 >> 06); - let w58 = w42 +% rot(w43, 01) ^ rot(w43, 08) ^ (w43 >> 07) +% w51 +% rot(w56, 19) ^ rot(w56, 61) ^ (w56 >> 06); - let w59 = w43 +% rot(w44, 01) ^ rot(w44, 08) ^ (w44 >> 07) +% w52 +% rot(w57, 19) ^ rot(w57, 61) ^ (w57 >> 06); - let w60 = w44 +% rot(w45, 01) ^ rot(w45, 08) ^ (w45 >> 07) +% w53 +% rot(w58, 19) ^ rot(w58, 61) ^ (w58 >> 06); - let w61 = w45 +% rot(w46, 01) ^ rot(w46, 08) ^ (w46 >> 07) +% w54 +% rot(w59, 19) ^ rot(w59, 61) ^ (w59 >> 06); - let w62 = w46 +% rot(w47, 01) ^ rot(w47, 08) ^ (w47 >> 07) +% w55 +% rot(w60, 19) ^ rot(w60, 61) ^ (w60 >> 06); - let w63 = w47 +% rot(w48, 01) ^ rot(w48, 08) ^ (w48 >> 07) +% w56 +% rot(w61, 19) ^ rot(w61, 61) ^ (w61 >> 06); - let w64 = w48 +% rot(w49, 01) ^ rot(w49, 08) ^ (w49 >> 07) +% w57 +% rot(w62, 19) ^ rot(w62, 61) ^ (w62 >> 06); - let w65 = w49 +% rot(w50, 01) ^ rot(w50, 08) ^ (w50 >> 07) +% w58 +% rot(w63, 19) ^ rot(w63, 61) ^ (w63 >> 06); - let w66 = w50 +% rot(w51, 01) ^ rot(w51, 08) ^ (w51 >> 07) +% w59 +% rot(w64, 19) ^ rot(w64, 61) ^ (w64 >> 06); - let w67 = w51 +% rot(w52, 01) ^ rot(w52, 08) ^ (w52 >> 07) +% w60 +% rot(w65, 19) ^ rot(w65, 61) ^ (w65 >> 06); - let w68 = w52 +% rot(w53, 01) ^ rot(w53, 08) ^ (w53 >> 07) +% w61 +% rot(w66, 19) ^ rot(w66, 61) ^ (w66 >> 06); - let w69 = w53 +% rot(w54, 01) ^ rot(w54, 08) ^ (w54 >> 07) +% w62 +% rot(w67, 19) ^ rot(w67, 61) ^ (w67 >> 06); - let w70 = w54 +% rot(w55, 01) ^ rot(w55, 08) ^ (w55 >> 07) +% w63 +% rot(w68, 19) ^ rot(w68, 61) ^ (w68 >> 06); - let w71 = w55 +% rot(w56, 01) ^ rot(w56, 08) ^ (w56 >> 07) +% w64 +% rot(w69, 19) ^ rot(w69, 61) ^ (w69 >> 06); - let w72 = w56 +% rot(w57, 01) ^ rot(w57, 08) ^ (w57 >> 07) +% w65 +% rot(w70, 19) ^ rot(w70, 61) ^ (w70 >> 06); - let w73 = w57 +% rot(w58, 01) ^ rot(w58, 08) ^ (w58 >> 07) +% w66 +% rot(w71, 19) ^ rot(w71, 61) ^ (w71 >> 06); - let w74 = w58 +% rot(w59, 01) ^ rot(w59, 08) ^ (w59 >> 07) +% w67 +% rot(w72, 19) ^ rot(w72, 61) ^ (w72 >> 06); - let w75 = w59 +% rot(w60, 01) ^ rot(w60, 08) ^ (w60 >> 07) +% w68 +% rot(w73, 19) ^ rot(w73, 61) ^ (w73 >> 06); - let w76 = w60 +% rot(w61, 01) ^ rot(w61, 08) ^ (w61 >> 07) +% w69 +% rot(w74, 19) ^ rot(w74, 61) ^ (w74 >> 06); - let w77 = w61 +% rot(w62, 01) ^ rot(w62, 08) ^ (w62 >> 07) +% w70 +% rot(w75, 19) ^ rot(w75, 61) ^ (w75 >> 06); - let w78 = w62 +% rot(w63, 01) ^ rot(w63, 08) ^ (w63 >> 07) +% w71 +% rot(w76, 19) ^ rot(w76, 61) ^ (w76 >> 06); - let w79 = w63 +% rot(w64, 01) ^ rot(w64, 08) ^ (w64 >> 07) +% w72 +% rot(w77, 19) ^ rot(w77, 61) ^ (w77 >> 06); - - // prettier-ignore - do { - t := h +% K.K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K09 +% w09 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K10 +% w10 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K11 +% w11 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K12 +% w12 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K13 +% w13 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K14 +% w14 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K64 +% w64 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K65 +% w65 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K66 +% w66 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K67 +% w67 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K68 +% w68 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K69 +% w69 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K70 +% w70 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K71 +% w71 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K72 +% w72 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K73 +% w73 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K74 +% w74 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K75 +% w75 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K76 +% w76 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K77 +% w77 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K78 +% w78 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K79 +% w79 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - }; - - // final addition - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - // counters - i += 128; - }; - // write state back to registers - state[0] := a; - state[1] := b; - state[2] := c; - state[3] := d; - state[4] := e; - state[5] := f; - state[6] := g; - state[7] := h; - - return i; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/whole_blocks/varArray.mo b/.mops/sha2@0.2.5/src/sha512/whole_blocks/varArray.mo deleted file mode 100644 index ec7e1e1..0000000 --- a/.mops/sha2@0.2.5/src/sha512/whole_blocks/varArray.mo +++ /dev/null @@ -1,227 +0,0 @@ -import Prim "mo:prim"; -import K "../constants"; - -module { - func rot(x : Nat64, y : Nat64) : Nat64 = x <>> y; - - let nat32To64 = Prim.nat32ToNat64; - let nat16To32 = Prim.nat16ToNat32; - let nat8To16 = Prim.nat8ToNat16; - - /// Run the SHA512 compression on every full 128-byte block in `data` from index `start` to the end. Returns the index just past the last block consumed (i.e. `start + 128 * blocks`). - public func process_blocks(state : [var Nat64], data : [var Nat8], start : Nat) : Nat { - let sz = data.size(); - var i = start; - // load state registers - var a = state[0]; - var b = state[1]; - var c = state[2]; - var d = state[3]; - var e = state[4]; - var f = state[5]; - var g = state[6]; - var h = state[7]; - var t = 0 : Nat64; - var i_max : Nat = i + ((sz - i) / 128) * 128; - while (i < i_max) { - let a_0 = a; - let b_0 = b; - let c_0 = c; - let d_0 = d; - let e_0 = e; - let f_0 = f; - let g_0 = g; - let h_0 = h; - - let w00 = nat32To64(nat16To32(nat8To16(data[i + 0]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 1]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 2]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 3]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 4]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 5]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 6]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 7]))); - let w01 = nat32To64(nat16To32(nat8To16(data[i + 8]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 9]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 10]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 11]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 12]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 13]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 14]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 15]))); - let w02 = nat32To64(nat16To32(nat8To16(data[i + 16]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 17]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 18]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 19]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 20]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 21]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 22]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 23]))); - let w03 = nat32To64(nat16To32(nat8To16(data[i + 24]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 25]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 26]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 27]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 28]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 29]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 30]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 31]))); - let w04 = nat32To64(nat16To32(nat8To16(data[i + 32]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 33]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 34]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 35]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 36]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 37]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 38]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 39]))); - let w05 = nat32To64(nat16To32(nat8To16(data[i + 40]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 41]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 42]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 43]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 44]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 45]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 46]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 47]))); - let w06 = nat32To64(nat16To32(nat8To16(data[i + 48]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 49]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 50]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 51]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 52]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 53]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 54]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 55]))); - let w07 = nat32To64(nat16To32(nat8To16(data[i + 56]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 57]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 58]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 59]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 60]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 61]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 62]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 63]))); - let w08 = nat32To64(nat16To32(nat8To16(data[i + 64]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 65]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 66]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 67]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 68]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 69]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 70]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 71]))); - let w09 = nat32To64(nat16To32(nat8To16(data[i + 72]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 73]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 74]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 75]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 76]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 77]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 78]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 79]))); - let w10 = nat32To64(nat16To32(nat8To16(data[i + 80]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 81]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 82]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 83]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 84]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 85]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 86]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 87]))); - let w11 = nat32To64(nat16To32(nat8To16(data[i + 88]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 89]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 90]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 91]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 92]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 93]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 94]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 95]))); - let w12 = nat32To64(nat16To32(nat8To16(data[i + 96]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 97]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 98]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 99]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 100]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 101]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 102]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 103]))); - let w13 = nat32To64(nat16To32(nat8To16(data[i + 104]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 105]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 106]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 107]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 108]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 109]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 110]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 111]))); - let w14 = nat32To64(nat16To32(nat8To16(data[i + 112]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 113]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 114]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 115]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 116]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 117]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 118]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 119]))); - let w15 = nat32To64(nat16To32(nat8To16(data[i + 120]))) << 56 | nat32To64(nat16To32(nat8To16(data[i + 121]))) << 48 | nat32To64(nat16To32(nat8To16(data[i + 122]))) << 40 | nat32To64(nat16To32(nat8To16(data[i + 123]))) << 32 | nat32To64(nat16To32(nat8To16(data[i + 124]))) << 24 | nat32To64(nat16To32(nat8To16(data[i + 125]))) << 16 | nat32To64(nat16To32(nat8To16(data[i + 126]))) << 8 | nat32To64(nat16To32(nat8To16(data[i + 127]))); - - let w16 = w00 +% rot(w01, 01) ^ rot(w01, 08) ^ (w01 >> 07) +% w09 +% rot(w14, 19) ^ rot(w14, 61) ^ (w14 >> 06); - let w17 = w01 +% rot(w02, 01) ^ rot(w02, 08) ^ (w02 >> 07) +% w10 +% rot(w15, 19) ^ rot(w15, 61) ^ (w15 >> 06); - let w18 = w02 +% rot(w03, 01) ^ rot(w03, 08) ^ (w03 >> 07) +% w11 +% rot(w16, 19) ^ rot(w16, 61) ^ (w16 >> 06); - let w19 = w03 +% rot(w04, 01) ^ rot(w04, 08) ^ (w04 >> 07) +% w12 +% rot(w17, 19) ^ rot(w17, 61) ^ (w17 >> 06); - let w20 = w04 +% rot(w05, 01) ^ rot(w05, 08) ^ (w05 >> 07) +% w13 +% rot(w18, 19) ^ rot(w18, 61) ^ (w18 >> 06); - let w21 = w05 +% rot(w06, 01) ^ rot(w06, 08) ^ (w06 >> 07) +% w14 +% rot(w19, 19) ^ rot(w19, 61) ^ (w19 >> 06); - let w22 = w06 +% rot(w07, 01) ^ rot(w07, 08) ^ (w07 >> 07) +% w15 +% rot(w20, 19) ^ rot(w20, 61) ^ (w20 >> 06); - let w23 = w07 +% rot(w08, 01) ^ rot(w08, 08) ^ (w08 >> 07) +% w16 +% rot(w21, 19) ^ rot(w21, 61) ^ (w21 >> 06); - let w24 = w08 +% rot(w09, 01) ^ rot(w09, 08) ^ (w09 >> 07) +% w17 +% rot(w22, 19) ^ rot(w22, 61) ^ (w22 >> 06); - let w25 = w09 +% rot(w10, 01) ^ rot(w10, 08) ^ (w10 >> 07) +% w18 +% rot(w23, 19) ^ rot(w23, 61) ^ (w23 >> 06); - let w26 = w10 +% rot(w11, 01) ^ rot(w11, 08) ^ (w11 >> 07) +% w19 +% rot(w24, 19) ^ rot(w24, 61) ^ (w24 >> 06); - let w27 = w11 +% rot(w12, 01) ^ rot(w12, 08) ^ (w12 >> 07) +% w20 +% rot(w25, 19) ^ rot(w25, 61) ^ (w25 >> 06); - let w28 = w12 +% rot(w13, 01) ^ rot(w13, 08) ^ (w13 >> 07) +% w21 +% rot(w26, 19) ^ rot(w26, 61) ^ (w26 >> 06); - let w29 = w13 +% rot(w14, 01) ^ rot(w14, 08) ^ (w14 >> 07) +% w22 +% rot(w27, 19) ^ rot(w27, 61) ^ (w27 >> 06); - let w30 = w14 +% rot(w15, 01) ^ rot(w15, 08) ^ (w15 >> 07) +% w23 +% rot(w28, 19) ^ rot(w28, 61) ^ (w28 >> 06); - let w31 = w15 +% rot(w16, 01) ^ rot(w16, 08) ^ (w16 >> 07) +% w24 +% rot(w29, 19) ^ rot(w29, 61) ^ (w29 >> 06); - let w32 = w16 +% rot(w17, 01) ^ rot(w17, 08) ^ (w17 >> 07) +% w25 +% rot(w30, 19) ^ rot(w30, 61) ^ (w30 >> 06); - let w33 = w17 +% rot(w18, 01) ^ rot(w18, 08) ^ (w18 >> 07) +% w26 +% rot(w31, 19) ^ rot(w31, 61) ^ (w31 >> 06); - let w34 = w18 +% rot(w19, 01) ^ rot(w19, 08) ^ (w19 >> 07) +% w27 +% rot(w32, 19) ^ rot(w32, 61) ^ (w32 >> 06); - let w35 = w19 +% rot(w20, 01) ^ rot(w20, 08) ^ (w20 >> 07) +% w28 +% rot(w33, 19) ^ rot(w33, 61) ^ (w33 >> 06); - let w36 = w20 +% rot(w21, 01) ^ rot(w21, 08) ^ (w21 >> 07) +% w29 +% rot(w34, 19) ^ rot(w34, 61) ^ (w34 >> 06); - let w37 = w21 +% rot(w22, 01) ^ rot(w22, 08) ^ (w22 >> 07) +% w30 +% rot(w35, 19) ^ rot(w35, 61) ^ (w35 >> 06); - let w38 = w22 +% rot(w23, 01) ^ rot(w23, 08) ^ (w23 >> 07) +% w31 +% rot(w36, 19) ^ rot(w36, 61) ^ (w36 >> 06); - let w39 = w23 +% rot(w24, 01) ^ rot(w24, 08) ^ (w24 >> 07) +% w32 +% rot(w37, 19) ^ rot(w37, 61) ^ (w37 >> 06); - let w40 = w24 +% rot(w25, 01) ^ rot(w25, 08) ^ (w25 >> 07) +% w33 +% rot(w38, 19) ^ rot(w38, 61) ^ (w38 >> 06); - let w41 = w25 +% rot(w26, 01) ^ rot(w26, 08) ^ (w26 >> 07) +% w34 +% rot(w39, 19) ^ rot(w39, 61) ^ (w39 >> 06); - let w42 = w26 +% rot(w27, 01) ^ rot(w27, 08) ^ (w27 >> 07) +% w35 +% rot(w40, 19) ^ rot(w40, 61) ^ (w40 >> 06); - let w43 = w27 +% rot(w28, 01) ^ rot(w28, 08) ^ (w28 >> 07) +% w36 +% rot(w41, 19) ^ rot(w41, 61) ^ (w41 >> 06); - let w44 = w28 +% rot(w29, 01) ^ rot(w29, 08) ^ (w29 >> 07) +% w37 +% rot(w42, 19) ^ rot(w42, 61) ^ (w42 >> 06); - let w45 = w29 +% rot(w30, 01) ^ rot(w30, 08) ^ (w30 >> 07) +% w38 +% rot(w43, 19) ^ rot(w43, 61) ^ (w43 >> 06); - let w46 = w30 +% rot(w31, 01) ^ rot(w31, 08) ^ (w31 >> 07) +% w39 +% rot(w44, 19) ^ rot(w44, 61) ^ (w44 >> 06); - let w47 = w31 +% rot(w32, 01) ^ rot(w32, 08) ^ (w32 >> 07) +% w40 +% rot(w45, 19) ^ rot(w45, 61) ^ (w45 >> 06); - let w48 = w32 +% rot(w33, 01) ^ rot(w33, 08) ^ (w33 >> 07) +% w41 +% rot(w46, 19) ^ rot(w46, 61) ^ (w46 >> 06); - let w49 = w33 +% rot(w34, 01) ^ rot(w34, 08) ^ (w34 >> 07) +% w42 +% rot(w47, 19) ^ rot(w47, 61) ^ (w47 >> 06); - let w50 = w34 +% rot(w35, 01) ^ rot(w35, 08) ^ (w35 >> 07) +% w43 +% rot(w48, 19) ^ rot(w48, 61) ^ (w48 >> 06); - let w51 = w35 +% rot(w36, 01) ^ rot(w36, 08) ^ (w36 >> 07) +% w44 +% rot(w49, 19) ^ rot(w49, 61) ^ (w49 >> 06); - let w52 = w36 +% rot(w37, 01) ^ rot(w37, 08) ^ (w37 >> 07) +% w45 +% rot(w50, 19) ^ rot(w50, 61) ^ (w50 >> 06); - let w53 = w37 +% rot(w38, 01) ^ rot(w38, 08) ^ (w38 >> 07) +% w46 +% rot(w51, 19) ^ rot(w51, 61) ^ (w51 >> 06); - let w54 = w38 +% rot(w39, 01) ^ rot(w39, 08) ^ (w39 >> 07) +% w47 +% rot(w52, 19) ^ rot(w52, 61) ^ (w52 >> 06); - let w55 = w39 +% rot(w40, 01) ^ rot(w40, 08) ^ (w40 >> 07) +% w48 +% rot(w53, 19) ^ rot(w53, 61) ^ (w53 >> 06); - let w56 = w40 +% rot(w41, 01) ^ rot(w41, 08) ^ (w41 >> 07) +% w49 +% rot(w54, 19) ^ rot(w54, 61) ^ (w54 >> 06); - let w57 = w41 +% rot(w42, 01) ^ rot(w42, 08) ^ (w42 >> 07) +% w50 +% rot(w55, 19) ^ rot(w55, 61) ^ (w55 >> 06); - let w58 = w42 +% rot(w43, 01) ^ rot(w43, 08) ^ (w43 >> 07) +% w51 +% rot(w56, 19) ^ rot(w56, 61) ^ (w56 >> 06); - let w59 = w43 +% rot(w44, 01) ^ rot(w44, 08) ^ (w44 >> 07) +% w52 +% rot(w57, 19) ^ rot(w57, 61) ^ (w57 >> 06); - let w60 = w44 +% rot(w45, 01) ^ rot(w45, 08) ^ (w45 >> 07) +% w53 +% rot(w58, 19) ^ rot(w58, 61) ^ (w58 >> 06); - let w61 = w45 +% rot(w46, 01) ^ rot(w46, 08) ^ (w46 >> 07) +% w54 +% rot(w59, 19) ^ rot(w59, 61) ^ (w59 >> 06); - let w62 = w46 +% rot(w47, 01) ^ rot(w47, 08) ^ (w47 >> 07) +% w55 +% rot(w60, 19) ^ rot(w60, 61) ^ (w60 >> 06); - let w63 = w47 +% rot(w48, 01) ^ rot(w48, 08) ^ (w48 >> 07) +% w56 +% rot(w61, 19) ^ rot(w61, 61) ^ (w61 >> 06); - let w64 = w48 +% rot(w49, 01) ^ rot(w49, 08) ^ (w49 >> 07) +% w57 +% rot(w62, 19) ^ rot(w62, 61) ^ (w62 >> 06); - let w65 = w49 +% rot(w50, 01) ^ rot(w50, 08) ^ (w50 >> 07) +% w58 +% rot(w63, 19) ^ rot(w63, 61) ^ (w63 >> 06); - let w66 = w50 +% rot(w51, 01) ^ rot(w51, 08) ^ (w51 >> 07) +% w59 +% rot(w64, 19) ^ rot(w64, 61) ^ (w64 >> 06); - let w67 = w51 +% rot(w52, 01) ^ rot(w52, 08) ^ (w52 >> 07) +% w60 +% rot(w65, 19) ^ rot(w65, 61) ^ (w65 >> 06); - let w68 = w52 +% rot(w53, 01) ^ rot(w53, 08) ^ (w53 >> 07) +% w61 +% rot(w66, 19) ^ rot(w66, 61) ^ (w66 >> 06); - let w69 = w53 +% rot(w54, 01) ^ rot(w54, 08) ^ (w54 >> 07) +% w62 +% rot(w67, 19) ^ rot(w67, 61) ^ (w67 >> 06); - let w70 = w54 +% rot(w55, 01) ^ rot(w55, 08) ^ (w55 >> 07) +% w63 +% rot(w68, 19) ^ rot(w68, 61) ^ (w68 >> 06); - let w71 = w55 +% rot(w56, 01) ^ rot(w56, 08) ^ (w56 >> 07) +% w64 +% rot(w69, 19) ^ rot(w69, 61) ^ (w69 >> 06); - let w72 = w56 +% rot(w57, 01) ^ rot(w57, 08) ^ (w57 >> 07) +% w65 +% rot(w70, 19) ^ rot(w70, 61) ^ (w70 >> 06); - let w73 = w57 +% rot(w58, 01) ^ rot(w58, 08) ^ (w58 >> 07) +% w66 +% rot(w71, 19) ^ rot(w71, 61) ^ (w71 >> 06); - let w74 = w58 +% rot(w59, 01) ^ rot(w59, 08) ^ (w59 >> 07) +% w67 +% rot(w72, 19) ^ rot(w72, 61) ^ (w72 >> 06); - let w75 = w59 +% rot(w60, 01) ^ rot(w60, 08) ^ (w60 >> 07) +% w68 +% rot(w73, 19) ^ rot(w73, 61) ^ (w73 >> 06); - let w76 = w60 +% rot(w61, 01) ^ rot(w61, 08) ^ (w61 >> 07) +% w69 +% rot(w74, 19) ^ rot(w74, 61) ^ (w74 >> 06); - let w77 = w61 +% rot(w62, 01) ^ rot(w62, 08) ^ (w62 >> 07) +% w70 +% rot(w75, 19) ^ rot(w75, 61) ^ (w75 >> 06); - let w78 = w62 +% rot(w63, 01) ^ rot(w63, 08) ^ (w63 >> 07) +% w71 +% rot(w76, 19) ^ rot(w76, 61) ^ (w76 >> 06); - let w79 = w63 +% rot(w64, 01) ^ rot(w64, 08) ^ (w64 >> 07) +% w72 +% rot(w77, 19) ^ rot(w77, 61) ^ (w77 >> 06); - - // prettier-ignore - do { - t := h +% K.K00 +% w00 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K01 +% w01 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K02 +% w02 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K03 +% w03 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K04 +% w04 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K05 +% w05 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K06 +% w06 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K07 +% w07 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K08 +% w08 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K09 +% w09 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K10 +% w10 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K11 +% w11 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K12 +% w12 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K13 +% w13 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K14 +% w14 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K15 +% w15 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K16 +% w16 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K17 +% w17 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K18 +% w18 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K19 +% w19 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K20 +% w20 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K21 +% w21 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K22 +% w22 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K23 +% w23 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K24 +% w24 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K25 +% w25 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K26 +% w26 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K27 +% w27 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K28 +% w28 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K29 +% w29 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K30 +% w30 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K31 +% w31 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K32 +% w32 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K33 +% w33 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K34 +% w34 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K35 +% w35 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K36 +% w36 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K37 +% w37 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K38 +% w38 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K39 +% w39 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K40 +% w40 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K41 +% w41 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K42 +% w42 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K43 +% w43 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K44 +% w44 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K45 +% w45 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K46 +% w46 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K47 +% w47 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K48 +% w48 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K49 +% w49 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K50 +% w50 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K51 +% w51 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K52 +% w52 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K53 +% w53 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K54 +% w54 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K55 +% w55 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K56 +% w56 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K57 +% w57 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K58 +% w58 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K59 +% w59 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K60 +% w60 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K61 +% w61 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K62 +% w62 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K63 +% w63 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K64 +% w64 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K65 +% w65 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K66 +% w66 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K67 +% w67 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K68 +% w68 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K69 +% w69 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K70 +% w70 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K71 +% w71 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K72 +% w72 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K73 +% w73 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K74 +% w74 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K75 +% w75 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K76 +% w76 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K77 +% w77 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K78 +% w78 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - t := h +% K.K79 +% w79 +% (e & f) ^ (^ e & g) +% rot(e, 14) ^ rot(e, 18) ^ rot(e, 41); h := g; g := f; f := e; e := d +% t; d := c; c := b; b := a; a := t +% (b & c) ^ (b & d) ^ (c & d) +% rot(a, 28) ^ rot(a, 34) ^ rot(a, 39); - }; - - // final addition - a +%= a_0; - b +%= b_0; - c +%= c_0; - d +%= d_0; - e +%= e_0; - f +%= f_0; - g +%= g_0; - h +%= h_0; - - // counters - i += 128; - }; - // write state back to registers - state[0] := a; - state[1] := b; - state[2] := c; - state[3] := d; - state[4] := e; - state[5] := f; - state[6] := g; - state[7] := h; - - return i; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/write/accessor.mo b/.mops/sha2@0.2.5/src/sha512/write/accessor.mo deleted file mode 100644 index f43d502..0000000 --- a/.mops/sha2@0.2.5/src/sha512/write/accessor.mo +++ /dev/null @@ -1,78 +0,0 @@ -import Nat64 "mo:core/Nat64"; -import Nat8 "mo:core/Nat8"; -import Prim "mo:prim"; -import ProcessBlock "../process_block"; -import Process "../whole_blocks/accessor"; -import Byte "byte"; - -module { - /// Internal SHA512 digest state used by the positional-accessor writer. - public type Digest = { - // msg buffer - msg : [var Nat64]; - var word : Nat64; - var i_msg : Nat8; - var i_byte : Nat8; - var i_block : Nat64; - // state variables - s : [var Nat64]; - }; - - /// Write `len` bytes obtained by calling `data(i)` for `i` in `[start, start + len)` into the SHA512 message buffer. - public func write(x : Digest, data : Nat -> Nat8, start : Nat, len : Nat) { - if (len == 0) return; - var pos = start; - let sz = start + len; // required absolute data size - if (x.i_msg > 0 or x.i_byte < 8) { - pos := write_data_to_buffer(x, data, sz, start); - }; - let end = Process.process_blocks(x.s, data, sz, pos); - x.i_block +%= Nat64.fromIntWrap(end - pos) / 128; - ignore write_data_to_buffer(x, data, sz, end); - }; - - // Write chunk of input data to buffer until either the block is full or the end of the input data is reached - // The return value refers to the input interval that was written in the form [start,end) - // at: random access function for input data - // sz: absolute data size for random access - // start: start index from which to read data in - func write_data_to_buffer(x : Digest, at : Nat -> Nat8, sz : Nat, start : Nat) : (end : Nat) { - if (start >= sz) return start; - var i = start; - while (x.i_byte < 8) { - if (i == sz) return sz; - Byte.writeByte(x, at(i)); - i += 1; - }; - // round the remaining length of sz - i down to a multiple of 8 - let i_max : Nat = i + ((sz - i) / 8) * 8; - var i_msg = x.i_msg; - let msg = x.msg; - while (i < i_max) { - // prettier-ignore - msg[Nat8.toNat(i_msg)] := - Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(at(i)))) << 56 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(at(i+1)))) << 48 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(at(i+2)))) << 40 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(at(i+3)))) << 32 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(at(i+4)))) << 24 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(at(i+5)))) << 16 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(at(i+6)))) << 8 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(at(i+7)))); - i += 8; - i_msg +%= 1; - if (i_msg == 16) { - ProcessBlock.process_block_from_buffer(x.s, msg); - x.i_msg := 0; - x.i_block +%= 1; - return i; - }; - }; - x.i_msg := i_msg; - while (i < sz) { - Byte.writeByte(x, at(i)); - i += 1; - }; - return i; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/write/array.mo b/.mops/sha2@0.2.5/src/sha512/write/array.mo deleted file mode 100644 index 039e848..0000000 --- a/.mops/sha2@0.2.5/src/sha512/write/array.mo +++ /dev/null @@ -1,76 +0,0 @@ -import Nat64 "mo:core/Nat64"; -import Nat8 "mo:core/Nat8"; -import Prim "mo:prim"; -import ProcessBlock "../process_block"; -import Process "../whole_blocks/array"; -import Byte "byte"; - -module { - /// Internal SHA512 digest state used by the `[Nat8]` writer. - public type Digest = { - // msg buffer - msg : [var Nat64]; - var word : Nat64; - var i_msg : Nat8; - var i_byte : Nat8; - var i_block : Nat64; - // state variables - s : [var Nat64]; - }; - - /// Write the entire `[Nat8]` array into the SHA512 message buffer, processing full blocks as they fill up. - public func write(x : Digest, data : [Nat8]) { - let sz = data.size(); - if (sz == 0) return; - var pos = 0; - if (x.i_msg > 0 or x.i_byte < 8) { - pos := write_data_to_buffer(x, data, pos); - }; - let end = Process.process_blocks(x.s, data, pos); - x.i_block +%= Nat64.fromIntWrap(end - pos) / 128; - ignore write_data_to_buffer(x, data, end); - }; - - // Write blob to buffer until either the block is full or the end of the blob is reached - // The return value refers to the interval that was written in the form [start,end) - func write_data_to_buffer(x : Digest, data : [Nat8], start : Nat) : (end : Nat) { - let sz = data.size(); - if (start >= sz) return start; - var i = start; - while (x.i_byte < 8) { - if (i == sz) return sz; - Byte.writeByte(x, data[i]); - i += 1; - }; - // round the remaining length of sz - i down to a multiple of 8 - let i_max : Nat = i + ((sz - i) / 8) * 8; - var i_msg = x.i_msg; - let msg = x.msg; - while (i < i_max) { - // prettier-ignore - msg[Nat8.toNat(i_msg)] := - Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i]))) << 56 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+1]))) << 48 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+2]))) << 40 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+3]))) << 32 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+4]))) << 24 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+5]))) << 16 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+6]))) << 8 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+7]))); - i += 8; - i_msg +%= 1; - if (i_msg == 16) { - ProcessBlock.process_block_from_buffer(x.s, msg); - x.i_msg := 0; - x.i_block +%= 1; - return i; - }; - }; - x.i_msg := i_msg; - while (i < sz) { - Byte.writeByte(x, data[i]); - i += 1; - }; - return i; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/write/blob.mo b/.mops/sha2@0.2.5/src/sha512/write/blob.mo deleted file mode 100644 index bc0e772..0000000 --- a/.mops/sha2@0.2.5/src/sha512/write/blob.mo +++ /dev/null @@ -1,82 +0,0 @@ -import Nat64 "mo:core/Nat64"; -import Nat8 "mo:core/Nat8"; -import Prim "mo:prim"; -import ProcessBlock "../process_block"; -import Process "../whole_blocks/blob"; -import Byte "byte"; - -module { - /// Internal SHA512 digest state used by the Blob writer. - public type Digest = { - // msg buffer - msg : [var Nat64]; - var word : Nat64; - var i_msg : Nat8; - var i_byte : Nat8; - var i_block : Nat64; - // state variables - s : [var Nat64]; - }; - - /// Write the entire `Blob` into the SHA512 message buffer, processing full blocks as they fill up. - public func write(x : Digest, data : Blob) { - let sz = data.size(); - if (sz == 0) return; - var pos = 0; - if (x.i_msg > 0 or x.i_byte < 8) { - pos := write_data_to_buffer(x, data, pos); - }; - // Run whole blocks directly only when at least one full block remains, so a - // sub-block input skips process_blocks — which would otherwise write the - // 8-word state back (8 Nat64 boxes) for zero blocks. - if (pos + 128 <= sz) { - let end = Process.process_blocks(x.s, data, pos); - x.i_block +%= Nat64.fromIntWrap(end - pos) / 128; - pos := end; - }; - ignore write_data_to_buffer(x, data, pos); - }; - - // Write blob to buffer until either the block is full or the end of the blob is reached - // The return value refers to the interval that was written in the form [start,end) - func write_data_to_buffer(x : Digest, data : Blob, start : Nat) : (end : Nat) { - let sz = data.size(); - if (start >= sz) return start; - var i = start; - while (x.i_byte < 8) { - if (i == sz) return sz; - Byte.writeByte(x, data[i]); - i += 1; - }; - // round the remaining length of sz - i down to a multiple of 8 - let i_max : Nat = i + ((sz - i) / 8) * 8; - var i_msg = x.i_msg; - let msg = x.msg; - while (i < i_max) { - // prettier-ignore - msg[Nat8.toNat(i_msg)] := - Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i]))) << 56 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+1]))) << 48 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+2]))) << 40 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+3]))) << 32 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+4]))) << 24 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+5]))) << 16 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+6]))) << 8 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+7]))); - i += 8; - i_msg +%= 1; - if (i_msg == 16) { - ProcessBlock.process_block_from_buffer(x.s, msg); - x.i_msg := 0; - x.i_block +%= 1; - return i; - }; - }; - x.i_msg := i_msg; - while (i < sz) { - Byte.writeByte(x, data[i]); - i += 1; - }; - return i; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/write/byte.mo b/.mops/sha2@0.2.5/src/sha512/write/byte.mo deleted file mode 100644 index 20d3a6a..0000000 --- a/.mops/sha2@0.2.5/src/sha512/write/byte.mo +++ /dev/null @@ -1,42 +0,0 @@ -import Nat8 "mo:core/Nat8"; -import Prim "mo:prim"; -import ProcessBlock "../process_block"; - -module { - /// Internal SHA512 digest state used by the single-byte writer. - public type Digest = { - // msg buffer - msg : [var Nat64]; - var word : Nat64; - var i_msg : Nat8; - var i_byte : Nat8; - var i_block : Nat64; - - // state variables - s : [var Nat64]; - }; - - /// Append a single byte to the SHA512 message buffer, processing a full block if one completes. - public func writeByte(x : Digest, val : Nat8) : () { - var word = x.word; - word := (word << 8) ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(val))); - let i_byte = x.i_byte; - if (i_byte == 1) { - var i_msg = x.i_msg; - x.msg[Nat8.toNat(i_msg)] := word; - x.word := 0; - x.i_byte := 8; - i_msg +%= 1; - if (i_msg == 16) { - ProcessBlock.process_block_from_buffer(x.s, x.msg); - x.i_msg := 0; - x.i_block +%= 1; - } else { - x.i_msg := i_msg; - }; - } else { - x.i_byte := i_byte -% 1; - x.word := word; - }; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/write/iter.mo b/.mops/sha2@0.2.5/src/sha512/write/iter.mo deleted file mode 100644 index 2d8d854..0000000 --- a/.mops/sha2@0.2.5/src/sha512/write/iter.mo +++ /dev/null @@ -1,65 +0,0 @@ -import Prim "mo:prim"; -import ProcessBlock "../process_block"; -import Process "../whole_blocks/iter"; - -module { - - /// Internal SHA512 digest state used by the iterator writer. - public type Digest = { - // msg buffer - msg : [var Nat64]; - var word : Nat64; - var i_msg : Nat8; - var i_byte : Nat8; - var i_block : Nat64; - // state variables - s : [var Nat64]; - }; - - /// Consume bytes from `data` (an `() -> ?Nat8` iterator) into the SHA512 message buffer until `data` returns `null`. - public func write(x : Digest, data : () -> ?Nat8) { - if (x.i_msg != 0 or x.i_byte != 8) { - write_data_to_buffer(x, data); - if (x.i_msg == 16) { - ProcessBlock.process_block_from_buffer(x.s, x.msg); - x.i_msg := 0; - x.i_block +%= 1; - }; - }; - - if (x.i_msg != 0 or x.i_byte != 8) return; - - // must have buf.i_msg == 0 and buf.high == true here - // continue to try to read entire blocks at once from the iterator - - Process.process_blocks(x, data); - }; - - /// Fill the current SHA512 message buffer slot from the iterator without processing the block. Stops when the buffer is full or the iterator yields `null`. - public func write_data_to_buffer(x : Digest, data : () -> ?Nat8) { - let msg = x.msg; - var word = x.word; - var i_byte = x.i_byte; - var i_msg = x.i_msg; - label l loop { - switch (data()) { - case (?val) { - // The following is an inlined version of writeByte(val) - word := (word << 8) ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(val))); - i_byte -%= 1; - if (i_byte == 0) { - msg[Prim.nat8ToNat(i_msg)] := word; - word := 0; - i_byte := 8; - i_msg +%= 1; - if (i_msg == 16) break l; - }; - }; - case (null) break l; - }; - }; - x.word := word; - x.i_byte := i_byte; - x.i_msg := i_msg; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/write/lib.mo b/.mops/sha2@0.2.5/src/sha512/write/lib.mo deleted file mode 100644 index a354726..0000000 --- a/.mops/sha2@0.2.5/src/sha512/write/lib.mo +++ /dev/null @@ -1,52 +0,0 @@ -import Array "./array"; -import Blob "./blob"; -import VarArray "./varArray"; -import Accessor "./accessor"; -import Reader "./reader"; -import Iter "./iter"; - -module { - /// Internal SHA512 digest state shared by all writer dispatch functions. - public type Digest = { - // msg buffer - msg : [var Nat64]; - var word : Nat64; - var i_msg : Nat8; - var i_byte : Nat8; - var i_block : Nat64; - // state variables - s : [var Nat64]; - var closed : Bool; - }; - - /// Dispatch a `Blob` write to the SHA512 block processor. Traps if `x` is closed. - public func blob(x : Digest, data : Blob) { - assert not x.closed; - Blob.write(x, data); - }; - /// Dispatch a `[Nat8]` write to the SHA512 block processor. Traps if `x` is closed. - public func array(x : Digest, data : [Nat8]) { - assert not x.closed; - Array.write(x, data); - }; - /// Dispatch a `[var Nat8]` write to the SHA512 block processor. Traps if `x` is closed. - public func varArray(x : Digest, data : [var Nat8]) { - assert not x.closed; - VarArray.write(x, data); - }; - /// Dispatch a positional-accessor write (`len` bytes starting at `start`) to the SHA512 block processor. Traps if `x` is closed. - public func accessor(x : Digest, data : Nat -> Nat8, start : Nat, len : Nat) : () { - assert not x.closed; - Accessor.write(x, data, start, len); - }; - /// Dispatch a reader-function write (`len` calls to `data`) to the SHA512 block processor. Traps if `x` is closed. - public func reader(x : Digest, data : () -> Nat8, len : Nat) : () { - assert not x.closed; - Reader.write(x, data, len); - }; - /// Dispatch an iterator write (consumes until the iterator returns `null`) to the SHA512 block processor. Traps if `x` is closed. - public func iter(x : Digest, data : () -> ?Nat8) { - assert not x.closed; - Iter.write(x, data); - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/write/reader.mo b/.mops/sha2@0.2.5/src/sha512/write/reader.mo deleted file mode 100644 index dd0a981..0000000 --- a/.mops/sha2@0.2.5/src/sha512/write/reader.mo +++ /dev/null @@ -1,74 +0,0 @@ -import Nat64 "mo:core/Nat64"; -import Nat8 "mo:core/Nat8"; -import Prim "mo:prim"; -import ProcessBlock "../process_block"; -import Process "../whole_blocks/reader"; -import Byte "byte"; - -module { - /// Internal SHA512 digest state used by the reader-function writer. - public type Digest = { - // msg buffer - msg : [var Nat64]; - var word : Nat64; - var i_msg : Nat8; - var i_byte : Nat8; - var i_block : Nat64; - // state variables - s : [var Nat64]; - }; - - /// Write `sz` bytes obtained from `sz` calls to `data` into the SHA512 message buffer. - public func write(x : Digest, data : () -> Nat8, sz : Nat) { - if (sz == 0) return; - var pos = 0; - if (x.i_msg > 0 or x.i_byte < 8) { - pos := write_data_to_buffer(x, data, sz, pos); - }; - let end = Process.process_blocks(x.s, data, sz, pos); - x.i_block +%= Nat64.fromIntWrap(end - pos) / 128; - ignore write_data_to_buffer(x, data, sz, end); - }; - - // Write blob to buffer until either the block is full or the end of the blob is reached - // The return value refers to the interval that was written in the form [start,end) - func write_data_to_buffer(x : Digest, data : () -> Nat8, sz : Nat, start : Nat) : (end : Nat) { - if (start >= sz) return start; - var i = start; - while (x.i_byte < 8) { - if (i == sz) return sz; - Byte.writeByte(x, data()); - i += 1; - }; - // round the remaining length of sz - i down to a multiple of 8 - let i_max : Nat = i + ((sz - i) / 8) * 8; - var i_msg = x.i_msg; - let msg = x.msg; - while (i < i_max) { - // prettier-ignore - msg[Nat8.toNat(i_msg)] := - Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data()))) << 56 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data()))) << 48 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data()))) << 40 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data()))) << 32 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data()))) << 24 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data()))) << 16 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data()))) << 8 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data()))); - i += 8; - i_msg +%= 1; - if (i_msg == 16) { - ProcessBlock.process_block_from_buffer(x.s, msg); - x.i_msg := 0; - x.i_block +%= 1; - return i; - }; - }; - x.i_msg := i_msg; - while (i < sz) { - Byte.writeByte(x, data()); - i += 1; - }; - return i; - }; -}; diff --git a/.mops/sha2@0.2.5/src/sha512/write/varArray.mo b/.mops/sha2@0.2.5/src/sha512/write/varArray.mo deleted file mode 100644 index 7e18d3a..0000000 --- a/.mops/sha2@0.2.5/src/sha512/write/varArray.mo +++ /dev/null @@ -1,76 +0,0 @@ -import Nat64 "mo:core/Nat64"; -import Nat8 "mo:core/Nat8"; -import Prim "mo:prim"; -import ProcessBlock "../process_block"; -import Process "../whole_blocks/varArray"; -import Byte "byte"; - -module { - /// Internal SHA512 digest state used by the `[var Nat8]` writer. - public type Digest = { - // msg buffer - msg : [var Nat64]; - var word : Nat64; - var i_msg : Nat8; - var i_byte : Nat8; - var i_block : Nat64; - // state variables - s : [var Nat64]; - }; - - /// Write the entire `[var Nat8]` array into the SHA512 message buffer, processing full blocks as they fill up. - public func write(x : Digest, data : [var Nat8]) { - let sz = data.size(); - if (sz == 0) return; - var pos = 0; - if (x.i_msg > 0 or x.i_byte < 8) { - pos := write_data_to_buffer(x, data, pos); - }; - let end = Process.process_blocks(x.s, data, pos); - x.i_block +%= Nat64.fromIntWrap(end - pos) / 128; - ignore write_data_to_buffer(x, data, end); - }; - - // Write blob to buffer until either the block is full or the end of the blob is reached - // The return value refers to the interval that was written in the form [start,end) - func write_data_to_buffer(x : Digest, data : [var Nat8], start : Nat) : (end : Nat) { - let sz = data.size(); - if (start >= sz) return start; - var i = start; - while (x.i_byte < 8) { - if (i == sz) return sz; - Byte.writeByte(x, data[i]); - i += 1; - }; - // round the remaining length of sz - i down to a multiple of 8 - let i_max : Nat = i + ((sz - i) / 8) * 8; - var i_msg = x.i_msg; - let msg = x.msg; - while (i < i_max) { - // prettier-ignore - msg[Nat8.toNat(i_msg)] := - Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i]))) << 56 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+1]))) << 48 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+2]))) << 40 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+3]))) << 32 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+4]))) << 24 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+5]))) << 16 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+6]))) << 8 - ^ Prim.nat32ToNat64(Prim.nat16ToNat32(Prim.nat8ToNat16(data[i+7]))); - i += 8; - i_msg +%= 1; - if (i_msg == 16) { - ProcessBlock.process_block_from_buffer(x.s, msg); - x.i_msg := 0; - x.i_block +%= 1; - return i; - }; - }; - x.i_msg := i_msg; - while (i < sz) { - Byte.writeByte(x, data[i]); - i += 1; - }; - return i; - }; -}; diff --git a/.mops/xtended-numbers@0.3.1/LICENSE b/.mops/xtended-numbers@0.3.1/LICENSE deleted file mode 100644 index 174b5cc..0000000 --- a/.mops/xtended-numbers@0.3.1/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Ethan Celletti - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.mops/xtended-numbers@0.3.1/README.md b/.mops/xtended-numbers@0.3.1/README.md deleted file mode 100644 index 3311734..0000000 --- a/.mops/xtended-numbers@0.3.1/README.md +++ /dev/null @@ -1,306 +0,0 @@ -## Funding - -This library was originally incentivized by [ICDevs](https://ICDevs.org). You -can view more about the bounty on the -[forum](https://forum.dfinity.org/t/icdevs-org-bounty-18-cbor-and-candid-motoko-parser-3-000/11398) -or [website](https://icdevs.org/bounties/2022/02/22/CBOR-and-Candid-Motoko-Parser.html). The -bounty was funded by The ICDevs.org commuity and the award paid to -@Gekctek. If you use this library and gain value from it, please consider -a [donation](https://icdevs.org/donations.html) to ICDevs. - -# Overview - -This is a library that extends on the Motoko base library for numbers. Maily focuses on encoding of numbers and 16/32 bit precision floats - -# Package - -### MOPS - -``` -mops install xtended-numbers -``` - -To setup MOPS package manage, follow the instructions from the [MOPS Site](https://j4mwm-bqaaa-aaaam-qajbq-cai.ic0.app/) - -# API - -## FloatX - -`nearlyEqual(a: Float, b: Float, relativeTolerance: Float, absoluteTolerance: Float): Bool` - -Takes in 2 floats and compares them loosely according to the tolerances. Absolute tolerance is a max flat difference between the values. Relative tolerance is the max difference between the values based on the percentage of the max value. For example, given the values `nealyEqual(1, 5, .0001, .001)` the relative diff is `max(1, 5) * .0001` or `.0005` while the absolute diff is `.001` - -`fromFloat(float: Float, precision: FloatPrecision) : FloatX` - -Converts a `Float` to a `FloatX` with the specified precision - -`toFloat(fX: FloatX) : Float` - -Converts a `FloatX` to a `Float` - -`encode(buffer: Buffer.Buffer, value: FloatX, encoding: {#lsb; #msb})` - -Encodes a `FloatX` to bytes buffer - -`decode(bytes: Iter.Iter, precision: {#f16; #f32; #f64}, encoding: {#lsb; #msb}) : ?FloatX` - -Decodes a `FloatX` from an iteration of bytes. If null is returned, then there was an error decoding or an unexpected end of bytes - -## IntX - -`toText(value : Int) : Text` - -Converts an Int into a text representation. Outputs a decimal value (-?[0-9]+). - -`toTextAdvanced(value : Int, format : Format) : Text` - -Converts an Int into a text representation. Allows for the specification of the output format. - -`fromText(value : Text) : ?Int` - -Converts text representation of a decimal integer (-?[0-9]+). If the text cannot -be parsed as an integer, the value returned will be null. Same as calling the `fromTextAdvanced` -with the `#decimal` format and no seperator - -`fromTextAdvanced(value : Text, format : Format, seperator : ?Char) : ?Int` - -Converts text representation of an integer in a specified format. Optioanlly can specify the -seperator that should be ignored (',' for 1,000,000 or '\_' for 1_000_000). If the text cannot -be parsed as an integer, the value returned will be null - -`from64To8(value: Int64) : Int8` - -Conversion. Traps on overflow/underflow. - -`from64To16(value: Int64) : Int16` - -Conversion. Traps on overflow/underflow. - -`from64To32(value: Int64) : Int32` - -Conversion. Traps on overflow/underflow. - -`from64ToInt(value: Int64) : Int` - -Conversion. Traps on overflow/underflow. - -`from32To8(value: Int32) : Int8` - -Conversion. Traps on overflow/underflow. - -`from32To16(value: Int32) : Int16` - -Conversion. Traps on overflow/underflow. - -`from32To64(value: Int32) : Int64` - -Conversion. Traps on overflow/underflow. - -`from32ToInt(value: Int32) : Int` - -Conversion. Traps on overflow/underflow. - -`from16To8(value: Int16) : Int8` - -Conversion. Traps on overflow/underflow. - -`from16To32(value: Int16) : Int32` - -Conversion. Traps on overflow/underflow. - -`from16To64(value: Int16) : Int64` - -Conversion. Traps on overflow/underflow. - -`from16ToInt(value: Int16) : Int` - -Conversion. Traps on overflow/underflow. - -`from8To16(value: Int8) : Int16` - -Conversion. Traps on overflow/underflow. - -`from8To32(value: Int8) : Int32` - -Conversion. Traps on overflow/underflow. - -`from8To64(value: Int8) : Int64` - -Conversion. Traps on overflow/underflow. - -`from8ToInt(value: Int8) : Int` - -Conversion. Traps on overflow/underflow. - -`encodeInt(buffer: Buffer.Buffer, value: Int, encoding: {#signedLEB128})` - -Encodes the specified value into the byte buffer - -`encodeInt8(buffer: Buffer.Buffer, value: Int8)` - -Encodes the specified value into the byte buffer - -`encodeInt16(buffer: Buffer.Buffer, value: Int16, encoding: {#lsb; #msb})` - -Encodes the specified value into the byte buffer - -`encodeInt32(buffer: Buffer.Buffer, value: Int32, encoding: {#lsb; #msb})` - -Encodes the specified value into the byte buffer - -`encodeInt64(buffer: Buffer.Buffer, value: Int64, encoding: {#lsb; #msb})` - -Encodes the specified value into the byte buffer - -`decodeInt(bytes: Iter.Iter, encoding: {#signedLEB128}) : ?Int` - -Decodes the iteration of bytes into a value. If invalid bytes, null will be returned - -`decodeInt8(bytes: Iter.Iter, encoding: {#lsb; #msb}) : ?Int8` - -Decodes the iteration of bytes into a value. If invalid bytes, null will be returned - -`decodeInt16(bytes: Iter.Iter, encoding: {#lsb; #msb}) : ?Int16` - -Decodes the iteration of bytes into a value. If invalid bytes, null will be returned - -`decodeInt32(bytes: Iter.Iter, encoding: {#lsb; #msb}) : ?Int32` - -Decodes the iteration of bytes into a value. If invalid bytes, null will be returned - -`decodeInt64(bytes: Iter.Iter, encoding: {#lsb; #msb}) : ?Int64` - -Decodes the iteration of bytes into a value. If invalid bytes, null will be returned - -## NatX - -`toText(value : Nat) : Text` - -Converts an Nat into a text representation. Outputs a decimal value (-?[0-9]+). - -`toTextAdvanced(value : Nat, format : Format) : Text` - -Converts an Nat into a text representation. Allows for the specification of the output format. - -`fromText(value : Text) : ?Nat` - -Converts text representation of a decimal positive integer (-?[0-9]+). If the text cannot -be parsed as an positive integer, the value returned will be null. Same as calling the `fromTextAdvanced` -with the `#decimal` format and no seperator - -`fromTextAdvanced(value : Text, format : Format, seperator : ?Char) : ?Nat` - -Converts text representation of an positive integer in a specified format. Optioanlly can specify the -seperator that should be ignored (',' for 1,000,000 or '\_' for 1_000_000). If the text cannot -be parsed as an positive integer, the value returned will be null - -`from64To8(value: Nat64) : Nat8` - -Conversion. Traps on overflow/underflow. - -`from64To16(value: Nat64) : Nat16` - -Conversion. Traps on overflow/underflow. - -`from64To32(value: Nat64) : Nat32` - -Conversion. Traps on overflow/underflow. - -`from64ToNat(value: Nat64) : Nat` - -Conversion. Traps on overflow/underflow. - -`from32To8(value: Nat32) : Nat8` - -Conversion. Traps on overflow/underflow. - -`from32To16(value: Nat32) : Nat16` - -Conversion. Traps on overflow/underflow. - -`from32To64(value: Nat32) : Nat64` - -Conversion. Traps on overflow/underflow. - -`from32ToNat(value: Nat32) : Nat` - -Conversion. Traps on overflow/underflow. - -`from16To8(value: Nat16) : Nat8` - -Conversion. Traps on overflow/underflow. - -`from16To32(value: Nat16) : Nat32` - -Conversion. Traps on overflow/underflow. - -`from16To64(value: Nat16) : Nat64` - -Conversion. Traps on overflow/underflow. - -`from16ToNat(value: Nat16) : Nat` - -Conversion. Traps on overflow/underflow. - -`from8To16(value: Nat8) : Nat16` - -Conversion. Traps on overflow/underflow. - -`from8To32(value: Nat8) : Nat32` - -Conversion. Traps on overflow/underflow. - -`from8To64(value: Nat8) : Nat64` - -Conversion. Traps on overflow/underflow. - -`from8ToNat(value: Nat8) : Nat` - -Conversion. Traps on overflow/underflow. - -`encodeNat(buffer: Buffer.Buffer, value: Nat, encoding: {#signedLEB128})` - -Encodes the specified value into the byte buffer - -`encodeNat8(buffer: Buffer.Buffer, value: Nat8)` - -Encodes the specified value into the byte buffer - -`encodeNat16(buffer: Buffer.Buffer, value: Nat16, encoding: {#lsb; #msb})` - -Encodes the specified value into the byte buffer - -`encodeNat32(buffer: Buffer.Buffer, value: Nat32, encoding: {#lsb; #msb})` - -Encodes the specified value into the byte buffer - -`encodeNat64(buffer: Buffer.Buffer, value: Nat64, encoding: {#lsb; #msb})` - -Encodes the specified value into the byte buffer - -`decodeNat(bytes: Iter.Iter, encoding: {#signedLEB128}) : ?Nat` - -Decodes the iteration of bytes into a value. If invalid bytes, null will be returned - -`decodeNat8(bytes: Iter.Iter, encoding: {#lsb; #msb}) : ?Nat8` - -Decodes the iteration of bytes into a value. If invalid bytes, null will be returned - -`decodeNat16(bytes: Iter.Iter, encoding: {#lsb; #msb}) : ?Nat16` - -Decodes the iteration of bytes into a value. If invalid bytes, null will be returned - -`decodeNat32(bytes: Iter.Iter, encoding: {#lsb; #msb}) : ?Nat32` - -Decodes the iteration of bytes into a value. If invalid bytes, null will be returned - -`decodeNat64(bytes: Iter.Iter, encoding: {#lsb; #msb}) : ?Nat64` - -Decodes the iteration of bytes into a value. If invalid bytes, null will be returned - - -# Testing - -``` -mops test -``` diff --git a/.mops/xtended-numbers@0.3.1/mops.toml b/.mops/xtended-numbers@0.3.1/mops.toml deleted file mode 100644 index a906521..0000000 --- a/.mops/xtended-numbers@0.3.1/mops.toml +++ /dev/null @@ -1,13 +0,0 @@ -[dependencies] -base = "0.11.1" - -[package] -name = "xtended-numbers" -version = "0.3.1" -description = "Extended functionality for motoko number types, such as byte encoding" -repository = "https://github.com/edjCase/motoko_numbers" -keywords = [ "numbers" ] -license = "MIT" - -[dev-dependencies] -test = "2.0.0" diff --git a/.mops/xtended-numbers@0.3.1/src/FloatX.mo b/.mops/xtended-numbers@0.3.1/src/FloatX.mo deleted file mode 100644 index efb4b26..0000000 --- a/.mops/xtended-numbers@0.3.1/src/FloatX.mo +++ /dev/null @@ -1,251 +0,0 @@ -import Buffer "mo:base/Buffer"; -import Float "mo:base/Float"; -import Int "mo:base/Int"; -import Int64 "mo:base/Int64"; -import Iter "mo:base/Iter"; -import Nat "mo:base/Nat"; -import Nat64 "mo:base/Nat64"; -import NatX "./NatX"; - -module { - - public type FloatPrecision = { #f16; #f32; #f64 }; - - public type FloatX = { - precision : FloatPrecision; - isNegative : Bool; - exponent : ?Int; - mantissa : Nat; - }; - - /// Compares two floating-point numbers for near equality within specified tolerances. - /// - /// ```motoko - /// let a : Float = 0.1; - /// let b : Float = 0.10000000000000001; - /// let result = nearlyEqual(a, b, 1e-15, 1e-15); - /// // result is true - /// ``` - public func nearlyEqual(a : Float, b : Float, relativeTolerance : Float, absoluteTolerance : Float) : Bool { - let maxAbsoluteValue : Float = Float.max(Float.abs(a), Float.abs(b)); - Float.abs(a -b) <= Float.max(relativeTolerance * maxAbsoluteValue, absoluteTolerance); - }; - - /// Converts a `Float` to a `FloatX` with the specified precision. - /// - /// ```motoko - /// let float : Float = 3.14159; - /// let floatX : FloatX = fromFloat(float, #f32); - /// ``` - public func fromFloat(float : Float, precision : FloatPrecision) : FloatX { - let bitInfo : PrecisionBitInfo = getPrecisionBitInfo(precision); - if (float == 0.0) { - return { - precision = precision; - isNegative = false; - exponent = null; - mantissa = 0; - }; - }; - let isNegative = float < 0; - - // maxMantissa = 2 ^ mantissaBitLength - // e = 2^exponent * (x + mantissa/maxMantissa) - // float = sign * e - // where x is 1 if exponent > 0 else 0 - // where sign is 1 if positive else -1 - - // Normal number are numbers that are represented by 2^minExponent -> 2^maxExponent - 1 - // Sub normal numbers are numbers represented by 2^minExponent * 1/maxMantissa -> 2^minExponent * (maxMantissa - 1)/maxMantissa - let isNormalNumber : Bool = Float.abs(float) >= bitInfo.smallestNormalNumber; - let (exponent : ?Int, x : Int) = if (isNormalNumber) { - // If is normal number then x is 1 - // e is 2^exponent + (number less than 2) - // so if you get the log2(e), truncate the remainder, it will represent the exponent - let e : Int = Float.toInt(Float.floor(Float.log(Float.abs(float)) / Float.log(2))); - (?e, 1); - } else { - // If smaller than 2^minExponent then x is 0 - // e is 2^exponent + (number less than 1) - // exponent is min value - var a = null; // TODO bug where this cant be a const - (a, 0); - }; - - // m = (|float|/2^exponent) - x - // mantissa = m * maxMantissa - // The m is the % of the exponent as the remainder between exponent and real value - let exp = switch (exponent) { - case (null) bitInfo.minExponent; // If null, its subnormal. use min exponent here - case (?e) e; - }; - let m : Float = (Float.abs(float) / calculateExponent(2, Float.fromInt(exp)) - Float.fromInt(x)); - // Mantissa represent how many offsets there are between the exponent and the value - let mantissa : Nat = Int.abs(Float.toInt(Float.nearest(m * Float.fromInt(bitInfo.maxMantissaDenomiator)))); - - { - precision = precision; - isNegative = isNegative; - exponent = exponent; - mantissa = mantissa; - }; - }; - - /// Converts a `FloatX` to a `Float`. - /// - /// ```motoko - /// let floatX : FloatX = { - /// precision = #f32; - /// isNegative = false; - /// exponent = ?1; - /// mantissa = 5033165; - /// }; - /// let float : Float = toFloat(floatX); - /// ``` - public func toFloat(fX : FloatX) : Float { - let bitInfo : PrecisionBitInfo = getPrecisionBitInfo(fX.precision); - - // e = 2^exponent * (x + mantissa/maxMantissa) - // float = sign * e - // where x is 1 if exponent > 0 else 0 - // where sign is 1 if positive else -1 - - let sign = if (fX.isNegative) -1.0 else 1.0; - let (exponent : Int, x : Nat) = switch (fX.exponent) { - case (null) (-14, 0); // If null, its subnormal. use min exponent here - case (?exponent) (exponent, 1); - }; - let expValue : Float = calculateExponent(2, Float.fromInt(exponent)); - sign * expValue * (Float.fromInt(x) + Float.fromInt(fX.mantissa) / Float.fromInt(bitInfo.maxMantissaDenomiator)); - }; - - /// Encodes a `FloatX` to a byte buffer. - /// - /// ```motoko - /// let floatX : FloatX = fromFloat(3.14159, #f32); - /// let buffer = Buffer.Buffer(4); - /// encode(buffer, floatX, #lsb); - /// ``` - public func encode(buffer : Buffer.Buffer, value : FloatX, encoding : { #lsb; #msb }) { - var bits : Nat64 = 0; - if (value.isNegative) { - bits |= 0x01; - }; - let bitInfo : PrecisionBitInfo = getPrecisionBitInfo(value.precision); - bits <<= Nat64.fromNat(bitInfo.exponentBitLength); - - let exponentBits : Nat64 = switch (value.exponent) { - case (null) 0; - case (?exponent) Int64.toNat64(Int64.fromInt(exponent + bitInfo.maxExponent)); - }; - bits |= exponentBits; - bits <<= Nat64.fromNat(bitInfo.mantissaBitLength); - let mantissaBits : Nat64 = Nat64.fromNat(value.mantissa); - bits |= mantissaBits; - - switch (value.precision) { - case (#f16) { - let nat16 = NatX.from64To16(bits); - NatX.encodeNat16(buffer, nat16, encoding); - }; - case (#f32) { - let nat32 = NatX.from64To32(bits); - NatX.encodeNat32(buffer, nat32, encoding); - }; - case (#f64) { - NatX.encodeNat64(buffer, bits, encoding); - }; - }; - }; - - /// Decodes a `FloatX` from an iteration of bytes. - /// - /// ```motoko - /// let bytes : [Nat8] = [64, 73, 15, 219]; // Encoded bytes for 3.14159 (f32) - /// let result = decode(bytes.vals(), #f32, #lsb); - /// switch (result) { - /// case (null) { /* Handle decoding error */ }; - /// case (?floatX) { /* Use decoded FloatX */ }; - /// }; - /// ``` - public func decode(bytes : Iter.Iter, precision : { #f16; #f32; #f64 }, encoding : { #lsb; #msb }) : ?FloatX { - do ? { - let bits : Nat64 = switch (precision) { - case (#f16) NatX.from16To64(NatX.decodeNat16(bytes, encoding)!); - case (#f32) NatX.from32To64(NatX.decodeNat32(bytes, encoding)!); - case (#f64) NatX.decodeNat64(bytes, encoding)!; - }; - let bitInfo : PrecisionBitInfo = getPrecisionBitInfo(precision); - if (bits == 0) { - return ?{ - precision = precision; - isNegative = false; - exponent = null; - mantissa = 0; - }; - }; - let (exponentBitLength : Nat64, mantissaBitLength : Nat64) = (Nat64.fromNat(bitInfo.exponentBitLength), Nat64.fromNat(bitInfo.mantissaBitLength)); - // Bitshift to get mantissa, exponent and sign bits - let mantissa : Nat = Nat64.toNat(bits & (2 ** mantissaBitLength - 1)); - // Extract out exponent bits with bitshift and mask - let exponentBits : Nat64 = (bits >> mantissaBitLength) & (2 ** exponentBitLength - 1); - let exponent : ?Int = if (exponentBits == 0) { - // If not bits are set, then it is sub normal - null; - } else { - // Get real exponent from the exponent bits - ?(Nat64.toNat(exponentBits) - bitInfo.maxExponent); - }; - let signBits : Nat64 = (bits >> (mantissaBitLength + exponentBitLength)) & 0x01; - - // Make negative if sign bit is 1 - let isNegative : Bool = signBits == 1; - { - precision = precision; - isNegative = isNegative; - exponent = exponent; - mantissa = mantissa; - }; - }; - }; - - private func calculateExponent(value : Float, exponent : Float) : Float { - if (exponent < 0) { - // Negative exponents arent allowed?? - // Have to do inverse of the exponent value - 1 / value ** (-1 * exponent); - } else { - value ** exponent; - }; - }; - - private type PrecisionBitInfo = { - exponentBitLength : Nat; - mantissaBitLength : Nat; - maxMantissaDenomiator : Nat; - minExponent : Int; - maxExponent : Int; - smallestNormalNumber : Float; - }; - - private func getPrecisionBitInfo(precision : FloatPrecision) : PrecisionBitInfo { - let (exponentBitLength : Nat, mantissaBitLength : Nat) = switch (precision) { - case (#f16) (5, 10); - case (#f32) (8, 23); - case (#f64) (11, 52); - }; - let maxExponent : Int = 2 ** (exponentBitLength - 1) - 1; - let minExponent : Int = -1 * (maxExponent - 1); - - let smallestNormalNumber : Float = calculateExponent(2, Float.fromInt(minExponent)); - { - exponentBitLength = exponentBitLength; - mantissaBitLength = mantissaBitLength; - minExponent = minExponent; - maxExponent = maxExponent; - maxMantissaDenomiator = 2 ** mantissaBitLength; - smallestNormalNumber = smallestNormalNumber; - }; - }; - -}; diff --git a/.mops/xtended-numbers@0.3.1/src/IntX.mo b/.mops/xtended-numbers@0.3.1/src/IntX.mo deleted file mode 100644 index 6b5f1aa..0000000 --- a/.mops/xtended-numbers@0.3.1/src/IntX.mo +++ /dev/null @@ -1,499 +0,0 @@ -import Buffer "mo:base/Buffer"; -import Int "mo:base/Int"; -import Int16 "mo:base/Int16"; -import Int32 "mo:base/Int32"; -import Int64 "mo:base/Int64"; -import Int8 "mo:base/Int8"; -import Iter "mo:base/Iter"; -import Nat64 "mo:base/Nat64"; -import Nat8 "mo:base/Nat8"; -import Nat "mo:base/Nat"; -import Array "mo:base/Array"; -import NatX "./NatX"; -import Util "./Util"; -import Text "mo:base/Text"; - -module { - public type Format = NatX.Format; - - /// Converts text representation of a decimal integer to an Int. - /// - /// ```motoko - /// let result = IntX.fromText("-123"); - /// switch (result) { - /// case (null) { /* Invalid input */ }; - /// case (?value) { /* value is -123 */ }; - /// }; - /// ``` - public func fromText(value : Text) : ?Int { - fromTextAdvanced(value, #decimal, null); - }; - - /// Converts text representation of an integer in a specified format to an Int. - /// - /// ```motoko - /// let result = IntX.fromTextAdvanced("-1010", #binary, null); - /// switch (result) { - /// case (null) { /* Invalid input */ }; - /// case (?value) { /* value is -10 */ }; - /// }; - /// ``` - public func fromTextAdvanced(value : Text, format : Format, seperator : ?Char) : ?Int { - do ? { - let isNegative = Text.startsWith(value, #char('-')); - let natTextValue = if (isNegative) { - // TODO better way to do substring? - let iter = value.chars(); - let _ = iter.next(); // Skip first char '-' - Text.fromIter(iter); // Negative sign, remove to make it a Nat - } else { - value; // No negative sign, use as is - }; - let natValue = NatX.fromTextAdvanced(natTextValue, format, seperator)!; - if (isNegative) { -1 * natValue } else { natValue }; // Revert to negative if was negative - }; - }; - - /// Converts an Int to its decimal text representation. - /// - /// ```motoko - /// let text = IntX.toText(-123); - /// // text is "-123" - /// ``` - public func toText(value : Int) : Text { - toTextAdvanced(value, #decimal); - }; - - /// Converts an Int to its text representation in a specified format. - /// - /// ```motoko - /// let text = IntX.toTextAdvanced(-10, #binary); - /// // text is "-1010" - /// ``` - public func toTextAdvanced(value : Int, format : Format) : Text { - let natValue : Nat = Int.abs(value); // Convert to nat to use NatX.toTextAdvanced - let isNegative = natValue != value; - let natTextValue = NatX.toTextAdvanced(natValue, format); - if (isNegative) { "-" # natTextValue } else { natTextValue }; // Add negative sign if negative - }; - - /// Converts Int64 to Int8. Traps on overflow/underflow. - /// - /// ```motoko - /// let value : Int64 = 127; - /// let result : Int8 = IntX.from64To8(value); - /// // result is 127 - /// ``` - public func from64To8(value : Int64) : Int8 { - Int8.fromInt(Int64.toInt(value)); - }; - - /// Converts Int64 to Int16. Traps on overflow/underflow. - /// - /// ```motoko - /// let value : Int64 = 32767; - /// let result : Int16 = IntX.from64To16(value); - /// // result is 32767 - /// ``` - public func from64To16(value : Int64) : Int16 { - Int16.fromInt(Int64.toInt(value)); - }; - - /// Converts Int64 to Int32. Traps on overflow/underflow. - /// - /// ```motoko - /// let value : Int64 = 2147483647; - /// let result : Int32 = IntX.from64To32(value); - /// // result is 2147483647 - /// ``` - public func from64To32(value : Int64) : Int32 { - Int32.fromInt(Int64.toInt(value)); - }; - - /// Converts Int64 to Int. - /// - /// ```motoko - /// let value : Int64 = 9223372036854775807; - /// let result : Int = IntX.from64ToInt(value); - /// // result is 9223372036854775807 - /// ``` - public func from64ToInt(value : Int64) : Int { - Int64.toInt(value); - }; - - /// Converts Int32 to Int8. Traps on overflow/underflow. - /// - /// ```motoko - /// let value : Int32 = 127; - /// let result : Int8 = IntX.from32To8(value); - /// // result is 127 - /// ``` - public func from32To8(value : Int32) : Int8 { - Int8.fromInt(Int32.toInt(value)); - }; - - /// Converts Int32 to Int16. Traps on overflow/underflow. - /// - /// ```motoko - /// let value : Int32 = 32767; - /// let result : Int16 = IntX.from32To16(value); - /// // result is 32767 - /// ``` - public func from32To16(value : Int32) : Int16 { - Int16.fromInt(Int32.toInt(value)); - }; - - /// Converts Int32 to Int64. - /// - /// ```motoko - /// let value : Int32 = 2147483647; - /// let result : Int64 = IntX.from32To64(value); - /// // result is 2147483647 - /// ``` - public func from32To64(value : Int32) : Int64 { - Int64.fromInt(Int32.toInt(value)); - }; - - /// Converts Int32 to Int. - /// - /// ```motoko - /// let value : Int32 = 2147483647; - /// let result : Int = IntX.from32ToInt(value); - /// // result is 2147483647 - /// ``` - public func from32ToInt(value : Int32) : Int { - Int32.toInt(value); - }; - - /// Converts Int16 to Int8. Traps on overflow/underflow. - /// - /// ```motoko - /// let value : Int16 = 127; - /// let result : Int8 = IntX.from16To8(value); - /// // result is 127 - /// ``` - public func from16To8(value : Int16) : Int8 { - Int8.fromInt(Int16.toInt(value)); - }; - - /// Converts Int16 to Int32. - /// - /// ```motoko - /// let value : Int16 = 32767; - /// let result : Int32 = IntX.from16To32(value); - /// // result is 32767 - /// ``` - public func from16To32(value : Int16) : Int32 { - Int32.fromInt(Int16.toInt(value)); - }; - - /// Converts Int16 to Int64. - /// - /// ```motoko - /// let value : Int16 = 32767; - /// let result : Int64 = IntX.from16To64(value); - /// // result is 32767 - /// ``` - public func from16To64(value : Int16) : Int64 { - Int64.fromInt(Int16.toInt(value)); - }; - - /// Converts Int16 to Int. - /// - /// ```motoko - /// let value : Int16 = 32767; - /// let result : Int = IntX.from16ToInt(value); - /// // result is 32767 - /// ``` - public func from16ToInt(value : Int16) : Int { - Int16.toInt(value); - }; - - /// Converts Int8 to Int16. - /// - /// ```motoko - /// let value : Int8 = 127; - /// let result : Int16 = IntX.from8To16(value); - /// // result is 127 - /// ``` - public func from8To16(value : Int8) : Int16 { - Int16.fromInt(Int8.toInt(value)); - }; - - /// Converts Int8 to Int32. - /// - /// ```motoko - /// let value : Int8 = 127; - /// let result : Int32 = IntX.from8To32(value); - /// // result is 127 - /// ``` - public func from8To32(value : Int8) : Int32 { - Int32.fromInt(Int8.toInt(value)); - }; - - /// Converts Int8 to Int64. - /// - /// ```motoko - /// let value : Int8 = 127; - /// let result : Int64 = IntX.from8To64(value); - /// // result is 127 - /// ``` - public func from8To64(value : Int8) : Int64 { - Int64.fromInt(Int8.toInt(value)); - }; - - /// Converts Int8 to Int. - /// - /// ```motoko - /// let value : Int8 = 127; - /// let result : Int = IntX.from8ToInt(value); - /// // result is 127 - /// ``` - public func from8ToInt(value : Int8) : Int { - Int8.toInt(value); - }; - - /// Encodes an Int to a byte buffer using signed LEB128 encoding. - /// - /// ```motoko - /// let buffer = Buffer.Buffer(8); - /// IntX.encodeInt(buffer, -123, #signedLEB128); - /// // buffer now contains the encoded bytes - /// ``` - public func encodeInt(buffer : Buffer.Buffer, value : Int, encoding : { #signedLEB128 }) { - switch (encoding) { - case (#signedLEB128) { - if (value == 0) { - buffer.add(0); - return; - }; - // Signed LEB128 - https://en.wikipedia.org/wiki/LEB128#Signed_LEB128 - // 11110001001000000 Binary encoding of 123456 - // 00001_11100010_01000000 As a 21-bit number (multiple of 7) - // 11110_00011101_10111111 Negating all bits (one's complement) - // 11110_00011101_11000000 Adding one (two's complement) (Binary encoding of signed -123456) - // 1111000 0111011 1000000 Split into 7-bit groups - //01111000 10111011 11000000 Add high 1 bits on all but last (most significant) group to form bytes - let positiveValue = Int.abs(value); - var bits : [Bool] = Util.natToLeastSignificantBits(positiveValue, 7, true); - if (value < 0) { - // If negative, then get twos compliment - bits := Util.twosCompliment(bits); - }; - Util.invariableLengthBytesEncode(buffer, bits); - }; - }; - }; - - /// Encodes an Int8 to a byte buffer. - /// - /// ```motoko - /// let buffer = Buffer.Buffer(1); - /// IntX.encodeInt8(buffer, -123); - /// // buffer now contains the encoded byte - /// ``` - public func encodeInt8(buffer : Buffer.Buffer, value : Int8) { - buffer.add(Int8.toNat8(value)); - }; - - /// Encodes an Int16 to a byte buffer. - /// - /// ```motoko - /// let buffer = Buffer.Buffer(2); - /// IntX.encodeInt16(buffer, -12345, #lsb); - /// // buffer now contains the encoded bytes - /// ``` - public func encodeInt16(buffer : Buffer.Buffer, value : Int16, encoding : { #lsb; #msb }) { - encodeIntX(buffer, Int64.fromInt(Int16.toInt(value)), encoding, #b16); - }; - - /// Encodes an Int32 to a byte buffer. - /// - /// ```motoko - /// let buffer = Buffer.Buffer(4); - /// IntX.encodeInt32(buffer, -1234567890, #lsb); - /// // buffer now contains the encoded bytes - /// ``` - public func encodeInt32(buffer : Buffer.Buffer, value : Int32, encoding : { #lsb; #msb }) { - encodeIntX(buffer, Int64.fromInt(Int32.toInt(value)), encoding, #b32); - }; - - /// Encodes an Int64 to a byte buffer. - /// - /// ```motoko - /// let buffer = Buffer.Buffer(8); - /// IntX.encodeInt64(buffer, -1234567890123456789, #lsb); - /// // buffer now contains the encoded bytes - /// ``` - public func encodeInt64(buffer : Buffer.Buffer, value : Int64, encoding : { #lsb; #msb }) { - encodeIntX(buffer, Int64.fromInt(Int64.toInt(value)), encoding, #b64); - }; - - /// Decodes an Int from a byte iterator using signed LEB128 encoding. - /// - /// ```motoko - /// let bytes : [Nat8] = [0xc6, 0xf5, 0x08]; // -123456 in signed LEB128 - /// let result = IntX.decodeInt(bytes.vals(), #signedLEB128); - /// switch (result) { - /// case (null) { /* Decoding error */ }; - /// case (?value) { /* value is -123456 */ }; - /// }; - /// ``` - public func decodeInt(bytes : Iter.Iter, encoding : { #signedLEB128 }) : ?Int { - do ? { - switch (encoding) { - case (#signedLEB128) { - var bits : [Bool] = Util.invariableLengthBytesDecode(bytes); - let isNegative = bits[bits.size() - 1]; - if (isNegative) { - // Reverse twos compliment - bits := Util.reverseTwosCompliment(bits); - }; - var i = 0; - let int = Array.foldLeft( - bits, - 0, - func(accum : Int, bit : Bool) { - let newAccum = if (bit) { - accum + Nat.pow(2, i); // Shift over 7 * i bits to get value to add, ignore first bit - } else { - accum; - }; - i += 1; - newAccum; - }, - ); - if (isNegative) { - int * -1; - } else { - int; - }; - }; - }; - }; - }; - /// Decodes an Int8 from a byte iterator. - /// - /// ```motoko - /// let bytes : [Nat8] = [0x85]; // -123 in two's complement - /// let result = IntX.decodeInt8(bytes.vals(), #lsb); - /// switch (result) { - /// case (null) { /* Decoding error */ }; - /// case (?value) { /* value is -123 */ }; - /// }; - /// ``` - public func decodeInt8(bytes : Iter.Iter, encoding : { #lsb; #msb }) : ?Int8 { - do ? { - let bits : [Bool] = decodeIntX(bytes, encoding, #b8)!; - bitsToInt(bits, 0, Int8.bitset); - }; - }; - - /// Decodes an Int16 from a byte iterator. - /// - /// ```motoko - /// let bytes : [Nat8] = [0x30, 0xcf]; // -12496 in little-endian - /// let result = IntX.decodeInt16(bytes.vals(), #lsb); - /// switch (result) { - /// case (null) { /* Decoding error */ }; - /// case (?value) { /* value is -12496 */ }; - /// }; - /// ``` - public func decodeInt16(bytes : Iter.Iter, encoding : { #lsb; #msb }) : ?Int16 { - do ? { - let bits : [Bool] = decodeIntX(bytes, encoding, #b16)!; - bitsToInt(bits, 0, Int16.bitset); - }; - }; - - /// Decodes an Int32 from a byte iterator. - /// - /// ```motoko - /// let bytes : [Nat8] = [0x2e, 0xf3, 0xff, 0xff]; // -3282 in little-endian - /// let result = IntX.decodeInt32(bytes.vals(), #lsb); - /// switch (result) { - /// case (null) { /* Decoding error */ }; - /// case (?value) { /* value is -3282 */ }; - /// }; - /// ``` - public func decodeInt32(bytes : Iter.Iter, encoding : { #lsb; #msb }) : ?Int32 { - do ? { - let bits : [Bool] = decodeIntX(bytes, encoding, #b32)!; - bitsToInt(bits, 0, Int32.bitset); - }; - }; - - /// Decodes an Int64 from a byte iterator. - /// - /// ```motoko - /// let bytes : [Nat8] = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]; // 9223372036854775807 in little-endian - /// let result = IntX.decodeInt64(bytes.vals(), #lsb); - /// switch (result) { - /// case (null) { /* Decoding error */ }; - /// case (?value) { /* value is 9223372036854775807 */ }; - /// }; - /// ``` - public func decodeInt64(bytes : Iter.Iter, encoding : { #lsb; #msb }) : ?Int64 { - do ? { - let bits : [Bool] = decodeIntX(bytes, encoding, #b64)!; - bitsToInt(bits, 0, Int64.bitset); - }; - }; - - private func decodeIntX(bytes : Iter.Iter, encoding : { #lsb; #msb }, size : { #b8; #b16; #b32; #b64 }) : ?[Bool] { - do ? { - let byteLength : Nat64 = getByteLength(size); - var nat64 : Nat64 = 0; - for (i in Iter.range(0, Nat64.toNat(byteLength) - 1)) { - let b : Nat8 = bytes.next()!; - let byteOffset : Nat64 = switch (encoding) { - case (#lsb) Nat64.fromNat(i); - case (#msb) Nat64.fromNat(Nat64.toNat(byteLength -1) - i); - }; - nat64 |= NatX.from8To64(b) << (byteOffset * 8); - }; - // Convert to bits in LSB order - var bits : [Bool] = Array.tabulate(Nat64.toNat(byteLength * 8), func(i : Nat) { Nat64.bittest(nat64, i) }); - bits; - }; - }; - - private func bitsToInt(bits : [Bool], initial : T, bitset : (T, Nat) -> T) : T { - var bitOffset = 0; - Array.foldLeft( - bits, - initial, - func(accum : T, x : Bool) { - let newAccum : T = if (not x) { - accum; // Dont set if 0 - } else { - bitset(accum, bitOffset); // Set if 1 - }; - bitOffset += 1; - newAccum; - }, - ); - }; - - private func getByteLength(size : { #b8; #b16; #b32; #b64 }) : Nat64 { - switch (size) { - case (#b8) 1; - case (#b16) 2; - case (#b32) 4; - case (#b64) 8; - }; - }; - - private func encodeIntX(buffer : Buffer.Buffer, value : Int64, encoding : { #lsb; #msb }, size : { #b16; #b32; #b64 }) { - let byteLength : Nat64 = getByteLength(size); - for (i in Iter.range(0, Nat64.toNat(byteLength) - 1)) { - let byteOffset : Int64 = switch (encoding) { - case (#lsb) Int64.fromInt(i); - case (#msb) Int64.fromInt(Nat64.toNat(byteLength - 1) - i); - }; - let byte : Int64 = (value >> (byteOffset * 8)) & 0xff; - buffer.add(Nat8.fromNat(Int.abs(Int64.toInt(byte)))); - }; - }; - -}; diff --git a/.mops/xtended-numbers@0.3.1/src/NatX.mo b/.mops/xtended-numbers@0.3.1/src/NatX.mo deleted file mode 100644 index e1a8b8f..0000000 --- a/.mops/xtended-numbers@0.3.1/src/NatX.mo +++ /dev/null @@ -1,532 +0,0 @@ -import Buffer "mo:base/Buffer"; -import Text "mo:base/Text"; -import Iter "mo:base/Iter"; -import Nat "mo:base/Nat"; -import Nat16 "mo:base/Nat16"; -import Nat32 "mo:base/Nat32"; -import Nat64 "mo:base/Nat64"; -import Nat8 "mo:base/Nat8"; -import Util "./Util"; -import Prelude "mo:base/Prelude"; - -module { - - public type Format = { #binary; #decimal; #hexadecimal }; - - /// Converts text representation of a decimal natural number to a Nat. - /// - /// ```motoko - /// let result = NatX.fromText("123"); - /// switch (result) { - /// case (null) { /* Invalid input */ }; - /// case (?value) { /* value is 123 */ }; - /// }; - /// ``` - public func fromText(value : Text) : ?Nat { - fromTextAdvanced(value, #decimal, null); - }; - - /// Converts text representation of a natural number in a specified format to a Nat. - /// - /// ```motoko - /// let result = NatX.fromTextAdvanced("1010", #binary, null); - /// switch (result) { - /// case (null) { /* Invalid input */ }; - /// case (?value) { /* value is 10 */ }; - /// }; - /// ``` - public func fromTextAdvanced(value : Text, format : Format, seperator : ?Char) : ?Nat { - if (value == "") { - return null; - }; - - let maxCharScalarValue = switch (format) { - case (#binary) 1; - case (#decimal) 9; - case (#hexadecimal) 15; - }; - let baseScalar = switch (format) { - case (#binary) 2; - case (#decimal) 10; - case (#hexadecimal) 16; - }; - - var nat : Nat = 0; - label f for (c in value.chars()) { - let charScalarValue = switch (c) { - case ('0') 0; - case ('1') 1; - case ('2') 2; - case ('3') 3; - case ('4') 4; - case ('5') 5; - case ('6') 6; - case ('7') 7; - case ('8') 8; - case ('9') 9; - - // TODO toLower? - case ('a') 10; - case ('A') 10; - - case ('b') 11; - case ('B') 11; - - case ('c') 12; - case ('C') 12; - - case ('d') 13; - case ('D') 13; - - case ('e') 14; - case ('E') 14; - - case ('f') 15; - case ('F') 15; - case (c) { - if (?c == seperator) { - continue f; // Skip seperator - }; - return null; - }; - }; - if (charScalarValue > maxCharScalarValue) { - // Invalid character such as 'A' being in - return null; - }; - // Shift scalar over to left by 1 (multiple by base) - // then add current digit - nat := (nat * baseScalar) + charScalarValue; - }; - ?nat; - }; - - /// Converts a Nat to its decimal text representation. - /// - /// ```motoko - /// let text = NatX.toText(123); - /// // text is "123" - /// ``` - public func toText(value : Nat) : Text { - toTextAdvanced(value, #decimal); - }; - - /// Converts a Nat to its text representation in a specified format. - /// - /// ```motoko - /// let text = NatX.toTextAdvanced(10, #binary); - /// // text is "1010" - /// ``` - public func toTextAdvanced(value : Nat, format : Format) : Text { - if (value == 0) { - return "0"; - }; - - let baseScalar = switch (format) { - case (#binary) 2; - case (#decimal) 10; - case (#hexadecimal) 16; - }; - - var buffer = Buffer.Buffer(5); - var remainingValue = value; - while (remainingValue > 0) { - let charScalarValue = remainingValue % baseScalar; // Get last digit - let c = switch (charScalarValue) { - case (0) '0'; - case (1) '1'; - case (2) '2'; - case (3) '3'; - case (4) '4'; - case (5) '5'; - case (6) '6'; - case (7) '7'; - case (8) '8'; - case (9) '9'; - - case (10) 'A'; - case (11) 'B'; - case (12) 'C'; - case (13) 'D'; - case (14) 'E'; - case (15) 'F'; - case (_) Prelude.unreachable(); - }; - buffer.add(c); - remainingValue := remainingValue / baseScalar; // Remove last digit - }; - Buffer.reverse(buffer); // Reverse because digits are from least to most significant - Text.fromIter(buffer.vals()); - }; - - /// Converts Nat64 to Nat8. Traps on overflow. - /// - /// ```motoko - /// let value : Nat64 = 255; - /// let result : Nat8 = NatX.from64To8(value); - /// // result is 255 - /// ``` - public func from64To8(value : Nat64) : Nat8 { - Nat8.fromNat(Nat64.toNat(value)); - }; - - /// Converts Nat64 to Nat16. Traps on overflow. - /// - /// ```motoko - /// let value : Nat64 = 65535; - /// let result : Nat16 = NatX.from64To16(value); - /// // result is 65535 - /// ``` - public func from64To16(value : Nat64) : Nat16 { - Nat16.fromNat(Nat64.toNat(value)); - }; - - /// Converts Nat64 to Nat32. Traps on overflow. - /// - /// ```motoko - /// let value : Nat64 = 4294967295; - /// let result : Nat32 = NatX.from64To32(value); - /// // result is 4294967295 - /// ``` - public func from64To32(value : Nat64) : Nat32 { - Nat32.fromNat(Nat64.toNat(value)); - }; - - /// Converts Nat64 to Nat. - /// - /// ```motoko - /// let value : Nat64 = 18446744073709551615; - /// let result : Nat = NatX.from64ToNat(value); - /// // result is 18446744073709551615 - /// ``` - public func from64ToNat(value : Nat64) : Nat { - Nat64.toNat(value); - }; - - /// Converts Nat32 to Nat8. Traps on overflow. - /// - /// ```motoko - /// let value : Nat32 = 255; - /// let result : Nat8 = NatX.from32To8(value); - /// // result is 255 - /// ``` - public func from32To8(value : Nat32) : Nat8 { - Nat8.fromNat(Nat32.toNat(value)); - }; - - /// Converts Nat32 to Nat16. Traps on overflow. - /// - /// ```motoko - /// let value : Nat32 = 65535; - /// let result : Nat16 = NatX.from32To16(value); - /// // result is 65535 - /// ``` - public func from32To16(value : Nat32) : Nat16 { - Nat16.fromNat(Nat32.toNat(value)); - }; - - /// Converts Nat32 to Nat64. - /// - /// ```motoko - /// let value : Nat32 = 4294967295; - /// let result : Nat64 = NatX.from32To64(value); - /// // result is 4294967295 - /// ``` - public func from32To64(value : Nat32) : Nat64 { - Nat64.fromNat(Nat32.toNat(value)); - }; - - /// Converts Nat32 to Nat. - /// - /// ```motoko - /// let value : Nat32 = 4294967295; - /// let result : Nat = NatX.from32ToNat(value); - /// // result is 4294967295 - /// ``` - public func from32ToNat(value : Nat32) : Nat { - Nat32.toNat(value); - }; - - /// Converts Nat16 to Nat8. Traps on overflow. - /// - /// ```motoko - /// let value : Nat16 = 255; - /// let result : Nat8 = NatX.from16To8(value); - /// // result is 255 - /// ``` - public func from16To8(value : Nat16) : Nat8 { - Nat8.fromNat(Nat16.toNat(value)); - }; - - /// Converts Nat16 to Nat32. - /// - /// ```motoko - /// let value : Nat16 = 65535; - /// let result : Nat32 = NatX.from16To32(value); - /// // result is 65535 - /// ``` - public func from16To32(value : Nat16) : Nat32 { - Nat32.fromNat(Nat16.toNat(value)); - }; - - /// Converts Nat16 to Nat64. - /// - /// ```motoko - /// let value : Nat16 = 65535; - /// let result : Nat64 = NatX.from16To64(value); - /// // result is 65535 - /// ``` - public func from16To64(value : Nat16) : Nat64 { - Nat64.fromNat(Nat16.toNat(value)); - }; - - /// Converts Nat16 to Nat. - /// - /// ```motoko - /// let value : Nat16 = 65535; - /// let result : Nat = NatX.from16ToNat(value); - /// // result is 65535 - /// ``` - public func from16ToNat(value : Nat16) : Nat { - Nat16.toNat(value); - }; - - /// Converts Nat8 to Nat16. - /// - /// ```motoko - /// let value : Nat8 = 255; - /// let result : Nat16 = NatX.from8To16(value); - /// // result is 255 - /// ``` - public func from8To16(value : Nat8) : Nat16 { - Nat16.fromNat(Nat8.toNat(value)); - }; - - /// Converts Nat8 to Nat32. - /// - /// ```motoko - /// let value : Nat8 = 255; - /// let result : Nat32 = NatX.from8To32(value); - /// // result is 255 - /// ``` - public func from8To32(value : Nat8) : Nat32 { - Nat32.fromNat(Nat8.toNat(value)); - }; - - /// Converts Nat8 to Nat64. - /// - /// ```motoko - /// let value : Nat8 = 255; - /// let result : Nat64 = NatX.from8To64(value); - /// // result is 255 - /// ``` - public func from8To64(value : Nat8) : Nat64 { - Nat64.fromNat(Nat8.toNat(value)); - }; - - /// Converts Nat8 to Nat. - /// - /// ```motoko - /// let value : Nat8 = 255; - /// let result : Nat = NatX.from8ToNat(value); - /// // result is 255 - /// ``` - public func from8ToNat(value : Nat8) : Nat { - Nat8.toNat(value); - }; - - /// Encodes a Nat to a byte buffer using unsigned LEB128 encoding. - /// - /// ```motoko - /// let buffer = Buffer.Buffer(8); - /// NatX.encodeNat(buffer, 123, #unsignedLEB128); - /// // buffer now contains the encoded bytes - /// ``` - public func encodeNat(buffer : Buffer.Buffer, value : Nat, encoding : { #unsignedLEB128 }) { - switch (encoding) { - case (#unsignedLEB128) { - if (value == 0) { - buffer.add(0); - return; - }; - // Unsigned LEB128 - https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128 - // 10011000011101100101 In raw binary - // 010011000011101100101 Padded to a multiple of 7 bits - // 0100110 0001110 1100101 Split into 7-bit groups - // 00100110 10001110 11100101 Add high 1 bits on all but last (most significant) group to form bytes - let bits : [Bool] = Util.natToLeastSignificantBits(value, 7, false); - - Util.invariableLengthBytesEncode(buffer, bits); - }; - }; - }; - - /// Encodes a Nat8 to a byte buffer. - /// - /// ```motoko - /// let buffer = Buffer.Buffer(1); - /// NatX.encodeNat8(buffer, 123); - /// // buffer now contains the encoded byte - /// ``` - public func encodeNat8(buffer : Buffer.Buffer, value : Nat8) { - buffer.add(value); - }; - - /// Encodes a Nat16 to a byte buffer. - /// - /// ```motoko - /// let buffer = Buffer.Buffer(2); - /// NatX.encodeNat16(buffer, 12345, #lsb); - /// // buffer now contains the encoded bytes - /// ``` - public func encodeNat16(buffer : Buffer.Buffer, value : Nat16, encoding : { #lsb; #msb }) { - encodeNatX(buffer, Nat64.fromNat(Nat16.toNat(value)), encoding, #b16); - }; - - /// Encodes a Nat32 to a byte buffer. - /// - /// ```motoko - /// let buffer = Buffer.Buffer(4); - /// NatX.encodeNat32(buffer, 1234567890, #lsb); - /// // buffer now contains the encoded bytes - /// ``` - public func encodeNat32(buffer : Buffer.Buffer, value : Nat32, encoding : { #lsb; #msb }) { - encodeNatX(buffer, Nat64.fromNat(Nat32.toNat(value)), encoding, #b32); - }; - - /// Encodes a Nat64 to a byte buffer. - /// - /// ```motoko - /// let buffer = Buffer.Buffer(8); - /// NatX.encodeNat64(buffer, 1234567890123456789, #lsb); - /// // buffer now contains the encoded bytes - /// ``` - public func encodeNat64(buffer : Buffer.Buffer, value : Nat64, encoding : { #lsb; #msb }) { - encodeNatX(buffer, value, encoding, #b64); - }; - - /// Decodes a Nat from a byte iterator using unsigned LEB128 encoding. - /// - /// ```motoko - /// let bytes : [Nat8] = [0xE5, 0x8E, 0x26]; // 624485 in unsigned LEB128 - /// let result = NatX.decodeNat(bytes.vals(), #unsignedLEB128); - /// switch (result) { - /// case (null) { /* Decoding error */ }; - /// case (?value) { /* value is 624485 */ }; - /// }; - /// ``` - public func decodeNat(bytes : Iter.Iter, _ : { #unsignedLEB128 }) : ?Nat { - do ? { - var v : Nat = 0; - var i : Nat = 0; - label l loop { - let byte : Nat8 = bytes.next()!; - v += Nat8.toNat(byte & 0x7f) * Nat.pow(2, 7 * i); // Shift over 7 * i bits to get value to add, ignore first bit - i += 1; - let hasNextByte = (byte & 0x80) == 0x80; // If starts with a 1, there is another byte - if (not hasNextByte) { - break l; - }; - }; - v; - }; - }; - - /// Decodes a Nat8 from a byte iterator. - /// - /// ```motoko - /// let bytes : [Nat8] = [123]; - /// let result = NatX.decodeNat8(bytes.vals(), #lsb); - /// switch (result) { - /// case (null) { /* Decoding error */ }; - /// case (?value) { /* value is 123 */ }; - /// }; - /// ``` - public func decodeNat8(bytes : Iter.Iter, _ : { #lsb; #msb }) : ?Nat8 { - bytes.next(); - }; - - /// Decodes a Nat16 from a byte iterator. - /// - /// ```motoko - /// let bytes : [Nat8] = [0x39, 0x30]; // 12345 in little-endian - /// let result = NatX.decodeNat16(bytes.vals(), #lsb); - /// switch (result) { - /// case (null) { /* Decoding error */ }; - /// case (?value) { /* value is 12345 */ }; - /// }; - /// ``` - public func decodeNat16(bytes : Iter.Iter, encoding : { #lsb; #msb }) : ?Nat16 { - do ? { - let value : Nat64 = decodeNatX(bytes, encoding, #b16)!; - from64To16(value); - }; - }; - - /// Decodes a Nat32 from a byte iterator. - /// - /// ```motoko - /// let bytes : [Nat8] = [0xD2, 0x02, 0x96, 0x49]; // 1234567890 in little-endian - /// let result = NatX.decodeNat32(bytes.vals(), #lsb); - /// switch (result) { - /// case (null) { /* Decoding error */ }; - /// case (?value) { /* value is 1234567890 */ }; - /// }; - /// ``` - public func decodeNat32(bytes : Iter.Iter, encoding : { #lsb; #msb }) : ?Nat32 { - do ? { - let value : Nat64 = decodeNatX(bytes, encoding, #b32)!; - from64To32(value); - }; - }; - - /// Decodes a Nat64 from a byte iterator. - /// - /// ```motoko - /// let bytes : [Nat8] = [0x15, 0x81, 0xE9, 0x7D, 0xF4, 0x10, 0x22, 0x11]; // 1234567890123456789 in little-endian - /// let result = NatX.decodeNat64(bytes.vals(), #lsb); - /// switch (result) { - /// case (null) { /* Decoding error */ }; - /// case (?value) { /* value is 1234567890123456789 */ }; - /// }; - /// ``` - public func decodeNat64(bytes : Iter.Iter, encoding : { #lsb; #msb }) : ?Nat64 { - decodeNatX(bytes, encoding, #b64); - }; - - private func decodeNatX(bytes : Iter.Iter, encoding : { #lsb; #msb }, size : { #b16; #b32; #b64 }) : ?Nat64 { - do ? { - let byteLength : Nat64 = getByteLength(size); - var nat64 : Nat64 = 0; - for (i in Iter.range(0, Nat64.toNat(byteLength) - 1)) { - let b = from8To64(bytes.next()!); - let byteOffset : Nat64 = switch (encoding) { - case (#lsb) Nat64.fromNat(i); - case (#msb) Nat64.fromNat(Nat64.toNat(byteLength -1) - i); - }; - nat64 |= b << (byteOffset * 8); - }; - nat64; - }; - }; - - private func encodeNatX(buffer : Buffer.Buffer, value : Nat64, encoding : { #lsb; #msb }, size : { #b16; #b32; #b64 }) { - let byteLength : Nat64 = getByteLength(size); - for (i in Iter.range(0, Nat64.toNat(byteLength) - 1)) { - let byteOffset : Nat64 = switch (encoding) { - case (#lsb) Nat64.fromNat(i); - case (#msb) Nat64.fromNat(Nat64.toNat(byteLength -1) - i); - }; - let byte : Nat8 = from64To8((value >> (byteOffset * 8)) & 0xff); - buffer.add(byte); - }; - }; - - private func getByteLength(size : { #b16; #b32; #b64 }) : Nat64 { - switch (size) { - case (#b16) 2; - case (#b32) 4; - case (#b64) 8; - }; - }; -}; diff --git a/.mops/xtended-numbers@0.3.1/src/Util.mo b/.mops/xtended-numbers@0.3.1/src/Util.mo deleted file mode 100644 index c3c12b2..0000000 --- a/.mops/xtended-numbers@0.3.1/src/Util.mo +++ /dev/null @@ -1,211 +0,0 @@ -import Array "mo:base/Array"; -import Buffer "mo:base/Buffer"; -import Char "mo:base/Char"; -import Int "mo:base/Int"; -import Iter "mo:base/Iter"; -import Nat8 "mo:base/Nat8"; -import Text "mo:base/Text"; - -module { - /// Converts a natural number to its binary representation as an array of booleans. - /// - /// ```motoko - /// let bits = Util.natToLeastSignificantBits(10, 8, false); - /// // bits is [false, true, false, true, false, false, false, false] - /// ``` - public func natToLeastSignificantBits(value : Nat, byteLength : Nat, hasSign : Bool) : [Bool] { - let buffer = Buffer.Buffer(64); - var remainingValue : Nat = value; - while (remainingValue > 0) { - let bit : Bool = remainingValue % 2 == 1; - buffer.add(bit); - remainingValue /= 2; - }; - while (buffer.size() % byteLength != 0) { - buffer.add(false); // Pad 0's for full byte - }; - if (hasSign) { - let mostSignificantBit : Bool = buffer.get(buffer.size() - 1); - if (mostSignificantBit) { - // If most significant bit is a 1, overflow to another byte - for (i in Iter.range(1, byteLength)) { - buffer.add(false); - }; - }; - }; - // Least Sigficant Bit first - Buffer.toArray(buffer); - }; - - /// Encodes an array of booleans into a buffer of bytes using invariable length encoding. - /// - /// ```motoko - /// let bits = [true, false, true, false, true, false, true, false]; - /// let buffer = Buffer.Buffer(1); - /// Util.invariableLengthBytesEncode(buffer, bits); - /// // buffer now contains [0x55] - /// ``` - public func invariableLengthBytesEncode(buffer : Buffer.Buffer, bits : [Bool]) { - - let byteCount : Nat = (bits.size() / 7) + (if (bits.size() % 7 != 0) 1 else 0); // 7, not 8, the 8th bit is to indicate end of number - - label f for (byteIndex in Iter.range(0, byteCount - 1)) { - var byte : Nat8 = 0; - for (bitOffset in Iter.range(0, 6)) { - let bit : Bool = bits[byteIndex * 7 + bitOffset]; - if (bit) { - // Set bit - byte := Nat8.bitset(byte, bitOffset); - }; - }; - let hasMoreBits = bits.size() > (byteIndex + 1) * 7; - if (hasMoreBits) { - // Have most left of byte be 1 if there is another byte - byte := Nat8.bitset(byte, 7); - }; - buffer.add(byte); - }; - }; - - /// Decodes a byte iterator into an array of booleans using invariable length decoding. - /// - /// ```motoko - /// let bytes : [Nat8] = [0x55]; - /// let bits = Util.invariableLengthBytesDecode(bytes.vals()); - /// // bits is [true, false, true, false, true, false, true] - /// ``` - public func invariableLengthBytesDecode(bytes : Iter.Iter) : [Bool] { - - let buffer = Buffer.Buffer(1); - label f for (byte in bytes) { - for (i in Iter.range(0, 6)) { - let bit = Nat8.bittest(byte, i); - buffer.add(bit); - }; - let hasNext = Nat8.bittest(byte, 7); - if (not hasNext) { - break f; - }; - }; - Buffer.toArray(buffer); - }; - - /// Performs two's complement on an array of booleans. - /// - /// ```motoko - /// let bits = [true, false, true, false]; - /// let complemented = Util.twosCompliment(bits); - /// // complemented is [true, true, false, true] - /// ``` - public func twosCompliment(bits : [Bool]) : [Bool] { - // Ones compliment, flip all bits - let flippedBits = Array.map(bits, func(b : Bool) : Bool { not b }); - - // Twos compliment, add 1 - let lastIndex : Nat = flippedBits.size() - 1; - let varBits : [var Bool] = Array.thaw(flippedBits); - - // Loop through adding 1 to the LSB, and carry the 1 if neccessary - label l for (n in Iter.range(0, lastIndex)) { - varBits[n] := not varBits[n]; // flip - if (varBits[n]) { - // If flipped to 1, end - break l; - } else { - // If flipped to 0, carry the one till the first 0 - }; - }; - Array.freeze(varBits); - }; - - /// Reverses the two's complement operation on an array of booleans. - /// - /// ```motoko - /// let bits = [true, true, false, true]; - /// let reversed = Util.reverseTwosCompliment(bits); - /// // reversed is [true, false, true, false] - /// ``` - public func reverseTwosCompliment(bits : [Bool]) : [Bool] { - // Reverse Twos compliment, remove 1 - // Find the 1 closest to the lsb, then convert it to 0 and everything toward lsb 1 - let varBits : [var Bool] = Array.thaw(bits); - label f for (n in Iter.range(0, bits.size() - 1)) { - let index = Int.abs(n); - if (varBits[index]) { - varBits[index] := false; - for (i in Iter.revRange(index -1, 0)) { - varBits[Int.abs(i)] := true; - }; - break f; - }; - }; - let newBits = Array.freeze(varBits); - - // Reverse Ones compliment, flip all bits - Array.map(newBits, func(b : Bool) : Bool { not b }); - }; - - /// Converts an array of booleans to a text representation. - /// - /// ```motoko - /// let bits = [true, false, true, false]; - /// let text = Util.bitsToText(bits, #msb); - /// // text is "0b1010" - /// ``` - public func bitsToText(bits : [Bool], order : { #lsb; #msb }) : Text { - let range = switch (order) { - case (#msb) Iter.range(0, bits.size() - 1); - case (#lsb) Iter.revRange(bits.size() - 1, 0); - }; - "0b" # Text.fromIter(Iter.map(range, func(i : Int) { if (bits[Int.abs(i)]) '1' else '0' })); - }; - - /// Converts an array of Nat8 to a hexadecimal string representation. - /// - /// ```motoko - /// let bytes : [Nat8] = [0x12, 0x34, 0xAB]; - /// let hexString = Util.toHexString(bytes); - /// // hexString is "0x12, 0x34, 0xAB" - /// ``` - public func toHexString(array : [Nat8]) : Text { - Array.foldLeft( - array, - "", - func(accum, w8) { - var pre = ""; - if (accum != "") { - pre #= ", "; - }; - accum # pre # encodeW8(w8); - }, - ); - }; - private let base : Nat8 = 0x10; - - private let symbols = [ - '0', - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - 'A', - 'B', - 'C', - 'D', - 'E', - 'F', - ]; - /** - * Encode an unsigned 8-bit integer in hexadecimal format. - */ - private func encodeW8(w8 : Nat8) : Text { - let c1 = symbols[Nat8.toNat(w8 / base)]; - let c2 = symbols[Nat8.toNat(w8 % base)]; - "0x" # Char.toText(c1) # Char.toText(c2); - }; -}; diff --git a/SECURITY.md b/SECURITY.md index 8a2ec70..a900cff 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -3,8 +3,18 @@ ## Reporting a vulnerability Please report vulnerabilities **privately** via GitHub's private vulnerability -reporting on this repository: **Security → Report a vulnerability** -(https://github.com/dfinity/multidex/security/advisories/new). +reporting on the public repository: **Security → Report a vulnerability** +(https://github.com/dfinity/public-multidex/security/advisories/new). + +If that is unavailable to you for any reason, email **multidex@dfinity.org** +instead. Either channel is private; use whichever you can reach. + +> This link previously pointed at `dfinity/multidex`, which is a **private** +> repository — so it 404'd for every external reporter, and there was no working +> private channel at all. Two independent review teams hit that wall in August +> 2026 and published in the open rather than sit on their findings; one withheld +> a user-targeting exploit for want of somewhere to send it. If a link here ever +> 404s for you, that is a bug in this file — mail the address above and say so. Please do **not** open public issues or pull requests for security problems, and do not test against deployments you do not operate beyond what is needed to diff --git a/candid/backend.did b/candid/backend.did index 57b33f9..c552d3f 100644 --- a/candid/backend.did +++ b/candid/backend.did @@ -892,6 +892,11 @@ service : { done: bool; folded: nat; }); + adminResetLiquidationBreaker: () -> (); + adminResetPlayAllowances: () -> (variant { + err: text; + ok: text; + }); adminRetryFuelNotify: (block: opt nat) -> (variant { err: text; ok: nat; @@ -1070,11 +1075,13 @@ service : { getAllTrades: (marketId: MarketId) -> (vec PublicTrade) query; getAmmAutoInventory: () -> (bool) query; getAmmBookShare: (marketId: MarketId) -> (opt AmmBookShare) query; + getAmmEverEnabled: () -> (bool) query; getAmmPool: (marketId: MarketId) -> (opt Pool) query; getAmmPools: () -> (vec Pool) query; getAmmPrincipal: () -> (principal) query; getAmmRebalanceEnabled: () -> (bool) query; getApiDoc: () -> (text) query; + getAppVersion: () -> (text) query; getArbStats: () -> (record { balances: vec record { @@ -1108,6 +1115,7 @@ service : { (CandleResponse) query; getCanisterInfo: () -> (record { + appVersion: text; arbCycles: nat; arbLifetimeTopUp: nat; archiveCanisterId: opt text; @@ -1181,6 +1189,16 @@ service : { nat; int; }) query; + getLiquidationSweepHealth: () -> + (record { + backoffNs: int; + completions: nat; + cursorActive: bool; + dispatches: nat; + failStreak: nat; + lastCompletedNs: int; + pending: nat; + }) query; getMarginHeatmap: (marketId: MarketId) -> (opt MarginHeatmap) query; getMarginHeatmapHistory: (marketId: MarketId, sinceNs: int) -> (vec MarginHeatmap) query; diff --git a/docs/bridge-and-cks-design.md b/docs/bridge-and-cks-design.md index 725dfd6..912bad5 100644 --- a/docs/bridge-and-cks-design.md +++ b/docs/bridge-and-cks-design.md @@ -123,7 +123,19 @@ However, sweeping occurs at intervals in pursuit of two goals: ## 11. Trust and custody — NNS-only upgradeability -The DEX, Bridge, and Archive canisters have **the NNS as their sole controller**. There is no administrator principal that can upgrade or drain them; any code change requires an on-chain NNS proposal and community vote (this is how the ckBTC/ckETH minters themselves are governed). Implications: +> **STATUS: TARGET STATE, NOT CURRENT STATE.** This section describes the custody +> model the production posture is designed to reach. It is **not** how the live +> deployment runs today. As of 2026-08-02 the DEX, Bridge and Archive canisters +> are controlled by **a single operator principal** (`docs/deploy-to-subnet.md` +> §2), there is no governance gate in the backend, and `resetExchange` → +> `performWorldWipe` will stop and delete every archive canister on any non- +> `#production` posture — including the live `#play` one. The honest statement +> for today is that the ledger is **tamper-evident but neither immutable nor +> authentic**: you can detect alteration, but a controller can still replace the +> record. Read the rest of this section in the future tense until the NNS +> handover in §12 has actually happened. + +Under the target model, the DEX, Bridge, and Archive canisters have **the NNS as their sole controller**. There is no administrator principal that can upgrade or drain them; any code change requires an on-chain NNS proposal and community vote (this is how the ckBTC/ckETH minters themselves are governed). Implications: - No insider key can move user funds; the threshold keys are only ever exercised by the Bridge's code, which is **NNS approved**. - Upgrades are deliberate and slow (proposal + voting period) — a feature for custody, but it means **no instant hotfix**, so the code must be right and the build **reproducible** so voters can verify the wasm matches the source. diff --git a/docs/deploy-to-subnet.md b/docs/deploy-to-subnet.md index be400b5..fdedef6 100644 --- a/docs/deploy-to-subnet.md +++ b/docs/deploy-to-subnet.md @@ -124,9 +124,9 @@ mops install && icp build # 2. Create + install on the dedicated subnet from the funded wallet. # (Create with a big cycle endowment; --subnet pins placement.) -icp deploy backend -e ic --subnet --identity --with-cycles -icp deploy bridge -e ic --subnet --identity -icp deploy frontend -e ic --subnet --identity +icp deploy backend -e subnet --subnet --identity --with-cycles +icp deploy bridge -e subnet --subnet --identity +icp deploy frontend -e subnet --subnet --identity # 3. Wire (re-apply after ANY reinstall — these live in stable vars): icp canister call backend setBridge "(principal \"\")" ... @@ -136,14 +136,14 @@ icp canister call bridge setDex "(principal \"\")" ... # 3b. Canister ENV VARS (the anti-sybil verifier reads these; they do NOT # apply to an existing canister on redeploy — use settings update): -icp canister settings update backend -e ic --identity \ +icp canister settings update backend -e subnet --identity \ --add-environment-variable "trusted_attribute_signers=rdmx6-jaaaa-aaaaa-aaadq-cai" \ --add-environment-variable "frontend_origins=https://.icp0.io,https://" # EVERY origin the app is served from must be listed, or Verify-with-Google # fails closed with #FrontendOriginMismatch (the error names the expected list). # 3c. AI key (can be done or ROTATED post-launch, any time — see scripts/set_ai_key.sh): -bash scripts/set_ai_key.sh --provider anthropic -e ic --identity # reads scripts/.anthropic-api-key, +bash scripts/set_ai_key.sh --provider anthropic -e subnet --identity # reads scripts/.anthropic-api-key, # $AI_API_KEY, --key-file, or hidden prompt; verifies aiConfigured()=true after. # LAST provider set WINS (anthropic switches the assistant off Gemini). Use a # HIGH-LIMIT key: aiComplete already rate-limits per principal (20/min · 200/h @@ -165,7 +165,7 @@ BACKEND= WALLET= ./scripts/topup.sh # tmux/systemd — the loops are plain bash): # scripts/.cloud-engine.conf must hold CE_IDENTITY= (the # funder); then: -IC_ENV=ic bash scripts/sim_trading.sh 12 2 +IC_ENV=subnet bash scripts/sim_trading.sh 12 2 # The runner now SELF-HEALS and BREATHES (2026-07-10): a background # replenisher polls every bot each 60s (public getTestBalance) and resets any # wallet leg below floor back to seed (cash <$10k→$40k, asset <$4k→$15k-at-mid) @@ -290,7 +290,7 @@ Print this. Each row has its detail section above. - [ ] **AI key**: `scripts/set_ai_key.sh --provider ` with the high-limit key; `aiConfigured()` = true (§3c) — rotatable post-launch any time - [ ] **Seed**: `deploy.sh cloud_seed` equivalent — history + $1M AMM + insurance; idempotent (§3.4) - [ ] **Cycles cron**: `topup.sh` every 15 min from the funded wallet (backend + archives + bridge) (§1) -- [ ] **Bots**: `IC_ENV=ic sim_trading.sh 12 2` under tmux/systemd with `CE_IDENTITY` in the conf; confirm mood line + first refills (§3.6) +- [ ] **Bots**: `IC_ENV=subnet sim_trading.sh 12 2` under tmux/systemd with `CE_IDENTITY` in the conf; confirm mood line + first refills (§3.6) - [ ] **Smoke**: run §6 end-to-end, ESPECIALLY the fresh-Google player journey on the canonical domain - [ ] **Alerting**: watch liquid headroom (balance − freezing limit), archive/bridge cycles via `getCanisterInfo`, and an OFF-CHAIN uptime check on the domain (a frozen canister can't report itself) - [ ] **Do NOT**: enable blackhole-at-seal; wire XRC; wire setFuelRoute; publish the raw canister URL as a sign-in surface diff --git a/docs/deployment-modes.md b/docs/deployment-modes.md index 3151a15..4271bff 100644 --- a/docs/deployment-modes.md +++ b/docs/deployment-modes.md @@ -40,11 +40,45 @@ posture. | `setTestScorecard`, `setTestShedFloor`, `setTestPendingJump`, `setTestMinSources`, `setTestXrcRate` (behavior/price hooks) | ✅ | ❌ | ❌ | test-determinism hooks; fairness/manipulation surface elsewhere | | `debugInspectByUsername` | ✅ | ❌ | ❌ | privacy — operator shouldn't read arbitrary accounts in public deployments | | `setTestBalance` / `bulkSetTestBalances` / `getTestBalance` (AdminOps, controller-only) | ✅ | ✅ | ❌ | operator bootstrap (vault seeding, sim wallets) still needed on play; every delta lands in `extNetFlow`, so it reads as CAPITAL, never as leaderboard profit | +| `injectHistoricalTrades` (chart backdrop, controller-only) | ✅ | ⏳ genesis only | ❌ | backdrop seeding is pre-launch only: accepted until the venue's first `enableAmm`, `#err` after — see "The genesis window" below | | `setTestTimersPaused` | ✅ | ✅ | ✅ | controller-only emergency brake, deliberately ungated | | `resetExchange`, `requoteAmm`, `fetchAndSetRefPrice`, `seedAmmPool` | ✅ | ✅ | ✅ | controller-only ops surface (season resets on play use `resetExchange`) | | `setXrcCanister` / `adminRefreshXrcAnchors` | ✅ | ✅ | ✅ | controller-only oracle wiring (see below) | | inspect: unknown-principal update calls | ✅ | ✅ | ❌ | a play user's FIRST call is `claimPlayFunds` — they can't be registered before it; production keeps the strict gate (registration happens off-ingress via the Bridge's `creditAndRegister`) | +## The genesis window (`injectHistoricalTrades` on #play) + +A #play venue wants a realistic multi-day chart backdrop at launch +(`play_start.sh` / `deploy.sh` seed it from CoinGecko hourly data), but the +August-audit invariant — *the operator does not move prices, forged or +otherwise* (OhShii #10.6b) — must hold once anyone can trade. The resolution +(decision 2026-08-06) is a one-way pre-launch window instead of the audit's +original blanket `#dev`-only gate, which had silently broken the #play +backdrop: + +- The stable latch `_ammEverEnabled` flips on the install's first + `enableAmm(_, true)` and never back. Before that, a controller may inject; + after, the call returns `#err("… genesis window closed …")`. On + `#production` it refuses always; on `#dev` it is unrestricted. +- **Survives upgrades and season resets.** `performWorldWipe` + (`resetExchange` / `resetSeason`) clears the pools map but deliberately not + the latch — a season reset cannot re-open the window. Only a reinstall (a + genuinely new venue) re-arms it. +- Installs upgraded from before the latch existed initialize it `false` + (persistence gotcha 1 below); `postupgrade` re-latches from any surviving + enabled pool. +- Ordering contract for bring-up scripts: inject history BEFORE the first + `enableAmm`. `play_start.sh` (step 4 → 5) and `deploy.sh` (step 2 → 4) + already comply, and both run on a fresh (re)install, so the window is open + when they inject. `inject_history.sh` probes the gate with an empty batch + before fetching anything and fails fast with the canister's own refusal — + a closed window is named as a posture fact, not misread as CoinGecko rate + limits. +- Refusal is a typed `#err`, not a trap — this method has a Result channel + (THE RULE at `requireDevHook` in `main.mo`). + +Pinned by `tests/test_audit_2026_08_fixes.sh` (posture branch). + ## claimPlayFunds semantics - One claim per principal for the **deployment's lifetime** — the claim diff --git a/docs/issue-triage-2026-08.md b/docs/issue-triage-2026-08.md new file mode 100644 index 0000000..4cf7fd1 --- /dev/null +++ b/docs/issue-triage-2026-08.md @@ -0,0 +1,528 @@ +# Community issue triage — August 2026 + +Triage of the eleven reports filed on `dfinity/public-multidex` (issues #2–#12) by two +outside teams — OhShii Labs (`rvnt9999`, #4–#11) and the Menese DeFi Team +(`KYounesMercatura`, #2, #3, #12) — between 2026-08-01 and 2026-08-02. **Every claim was re-verified against the +tree at `2fbbe86` by symbol lookup, not by trusting the reporters' line numbers** (the public +mirror is a single squashed snapshot, `0241cba`, so citations drift). Verdicts and the +evidence behind them are the basis for the points in [CONTRIBUTORS.md](../CONTRIBUTORS.md). + +**Headline: of ~40 distinct findings, essentially all were confirmed.** Four sub-claims were +refuted, all of them supporting prose rather than core findings. Two teams working +independently converged on the liquidation and oracle paths, which is itself signal. + +> **FIX STATUS (2026-08-02): §0, §1 and §2 are implemented, plus the §3 documentation +> corrections and §4.1–§4.2.** Every fix is pinned by a test that fails on the pre-fix tree — +> `tests/test_audit_2026_08_fixes.sh` (11 backend assertions), `tests/frontend_security.test.mjs` +> (53), `tests/test_deploy_hygiene.sh` (53, mutation-tested against 13 reintroduced bugs), +> `tests/test_archive_chain_paged.sh`, and additions to `tests/Liquidator.test.mo`, +> `tests/PriceFeed.test.mo` (~100 assertions), `tests/OrderBook.test.mo` and +> `tests/MatchingEngine.test.mo`. What is **not** done is listed in §6. +> +> **Correction (2026-08-03):** this line previously read "and all of §4". §4.3 (the uncapped +> sweeps — `sweepStaleUserOrders` has no per-call cap, and `tickTier`'s uptime and +> volume-badge walks are unsharded) has **no** code change and no test. §4.1 and §4.2 are +> closed. See §7 for the second wave of reports, which re-derived §4.3 independently. + +Verification also surfaced **three defects nobody reported** (§4) and **one finding whose +severity is higher than the reporter knew** (§1, dust griefing). + +A **second wave of reports (#13–#21) landed on 2026-08-02**, after this triage was written, +from a third reviewer (`andreij6`). They are triaged in §7, not above — several are explicit +corrections to conclusions recorded here. + +--- + +## 0. Fix this first: there is no private disclosure channel + +`SECURITY.md:7` points reporters at `https://github.com/dfinity/multidex/security/advisories/new`. +That repository is **private** (confirmed via the GitHub API: `private=true`); the public mirror +is `dfinity/public-multidex`. The link 404s for every external reporter. + +Consequences already realised: + +- All eleven reports landed **publicly**, including working exploit mechanics. +- OhShii Labs is **withholding a user-targeting delegation-phishing exploit** (§1.2) explicitly + because there is nowhere private to send it. They have asked for a channel. + +This is a one-line fix and it is blocking a real report. Do it before anything else. + +--- + +## 1. Fix now — live, user- or fund-affecting + +### 1.1 The in-browser ledger verifier has never verified anything +`src/frontend/src/ledger.js:139-147` passes a bare `Principal` where the SDK dispatches on +`{ canisterId }`. It throws on **every** call, in every environment; the throw is swallowed at +`:153-158` into `{ok: null}`; `verify()` still paints `lg-ok` and "✓ N links verified". +`index.html:2012` claims "nothing is taken on the exchange's word". A hostile replica can serve +a fabricated tape with `certificate = []` and the page ticks green. + +The identical bug was already diagnosed and fixed in `scripts/verify_ledger.mjs:430-435`, with +the reasoning written out in a comment. + +**Sequencing constraint (found in verification, not reported):** `main.js:975-978` calls +`fetchRootKey()` unconditionally with no hostname guard, so on mainnet the root key comes from +the host being verified. Today that has no consumer *because* the certificate check always +throws. **Fixing the certificate check alone promotes the root-key bug from latent to a live +"certificate VALID" bypass.** The two must land together. The CLI's guard +(`verify_ledger.mjs:59-64`, exact hostname match) is the pattern to port. + +Credit: OhShii #4.3 + #4.4. + +### 1.2 `ai-connect.html` mints an unrestricted, mis-displayed II delegation +All four structural claims confirmed, and verification **independently derived a working +exploit primitive** the reporters withheld: + +- `:61-63` concatenates an unvalidated `port` fragment param into the callback URL. A value of + `1@attacker.example.com` yields `http://127.0.0.1:1@attacker.example.com/callback`, which by + URL authority syntax resolves to the attacker's host. `:134-138` then **POSTs the signed II + delegation there**. +- `:62` sends `ttl_ns` to II while `:68` *displays* a different param, `ttl_hours` — the consent + shown need not match the delegation minted. +- `:109-113` requests **no `targets`**, so the delegation is valid for every canister. +- `:104` guards the inbound postMessage origin; nothing validates the outbound destination. The + page's CSP is `frame-ancestors 'none'` only, with no `connect-src`. + +**Not capped by `#play`.** This targets users, not the protocol; an II delegation is the user's +real identity credential and is unscoped here. Their decision to withhold was correct. + +Credit: OhShii #11.4. + +### 1.3 One dust payment makes a short pool permanently un-liquidatable +`pickCollateral` prefers ICPUSD unconditionally on `balance > 0` with no value floor; the +derived repay floors to zero; `writeOffLoan` rejects zero; the driver does `break L` instead of +trying the next collateral. The pool's real collateral is never examined, on every 30s sweep, +forever. `absorbBadDebt` never runs, so `getMarginRiskSummary` shows a solvent book. + +**Worse than reported, three ways** (the third found while writing the fix): +1. Not "exactly one base unit" — **any** dust below the debt token's whole-unit price triggers + it. The real window is **~$0.00168**, double the first estimate, because the derived repay + floors *twice* (once converting to debt units, once applying the penalty multiplier). +2. `pickCollateral`'s **same-token first pass** has the identical unconditional-balance defect — + a second independent route to the same trap, which the report treated as a bounded ≤1-unit leak. +3. **It is self-inflicting: no dust payment and no attacker are required.** The cross-token seize + refunds a remainder (`retainedColl` rounds up), and that refund is itself sub-threshold dust. + Measured on the pre-fix code: a pool holding $10,000 ICPUSD + $200,000 SOL has its ICPUSD + seized first, creating a 160,000-unit residue below the 168,000 threshold; the next iteration + picks that residue, floors to zero, and breaks — leaving **all 1000 SOL untouched** and + returning `#insolvent`. So `absorbBadDebt` fires and socialises a loss to the insurance fund + against a **fully-collateralised** position. That is worse than "un-liquidatable", and it + raises the trigger from "someone sends dust" to "any partial close whose seize is + balance-capped". + +Credit: OhShii #6.1 (refinements 1–3 found during verification and fix). + +### 1.4 The post-fill liquidation hook has no freshness guard +`adjustAffectedUsers` calls `tryLiquidate` with no `userMarksFreshAt` check, while both batch +call sites have one, and `tryLiquidate` performs none of its own — `marginPriceLookup` returns +a frozen `refPrice` of any age. The breaker *manufactures* the stale window: a pended jump +deliberately withholds `refPriceUpdatedNs`. + +`tryLiquidate`'s own comment is self-incriminating: *"Safe to call on any user... so callers +(post-fill hook, timer scan) don't need a pre-check."* Put the guard **inside** `tryLiquidate` +so no future call site can miss it. + +Credit: OhShii #6.2. + +### 1.5 A short cannot be closed in the 1.15–1.25 health band +`clampToInitialMargin` scores a fill on instantaneous LTV-weighted collateral delta and ignores +the debt repayment the fill triggers. Verification's algebra: `headroom` is **independent of +trade size** and is `≤ 0` exactly when health `≤ 1.25`, so `#partial` becomes unreachable and +every realistic close price is killed. Users are forced into the 5% liquidation penalty a +permitted close would have avoided. Two other sites assert the opposite invariant +("you must always be able to de-lever"; "closing is always allowed"). `gateInitialMargin` has +the escape clause the clamp lacks. + +Credit: OhShii #6.3. + +### 1.6 `runLiquidationBatch` is unbounded on a fund-safety path +Three phases, no cap, no cursor, no shard, no budget. `adminRunLiquidationBatch` calls the same +function, so a controller cannot recover past the threshold. `absorbBadDebt` has exactly one +call site, reachable only through `tryLiquidate`, so the risk panel stays green either way. + +**Open question, load-bearing for the failure signature — settle this on a replica.** Menese +measured a *silent* failure (stamps advance, work never completes), which requires +`ignore tickLiquidations()` to schedule a separate message. Two verification agents read the +source the other way (no `await` in the body ⇒ runs inline ⇒ the whole heartbeat traps). +Evidence favours **separate message**: `tickLiquidations` is declared plain `async`; moc 1.9.0 +refuses the call without send capability (`M0047`), which an inline computation would not need; +and Motoko provides `async*`/`await*` precisely as the non-message-sending alternative. + +**The remediation is identical either way** — shard with `lib/Shard.mo`, stamp `_lastLiqNs` on +*completion* rather than dispatch, add a consecutive-failure breaker. Only the symptom differs +(silent vs. whole-heartbeat stall). Cheap decisive test: force a trap inside `tickLiquidations` +locally and watch whether `_lastHeartbeatNs` advances. + +Credit: Menese #12.1. + +### 1.7 A trap in any synchronous heartbeat subtask stops maintenance permanently +Confirmed unisolated: `reapClosedOrders`, `drainLedgerJournal` (unthrottled, every beat), +`tickTier`, `tickHeatmaps`, `tickLeaderboardShard`, `settleInsuranceArrears` (unthrottled), +`tickCandleFill`, `tickDeadman` (unthrottled), `sweepStaleUserOrders`. All four aggravating +properties verified, including the self-reinforcing journal (`Accounts.mo:45` is the sole write +funnel; `List.clear` is only reachable after the full loop) and cadence alignment (every `HB_*` +constant divides 300s evenly). No consecutive-failure breaker exists anywhere. + +Good catch worth noting: `sweepStaleUserOrders` is declared `: Nat`, not `async`, so its +textual `ignore` defers nothing — the reporters classified it correctly *despite* the +misleading syntax. + +Credit: OhShii #5.7. + +### 1.8 A free identity can write unbounded permanent state +- `setUserPreferences` — no cap on the array, the strings, or anything else; never cleared by + `performWorldWipe`; no admin purge. Reachable **before** any anti-Sybil control applies. +- `createMarginPool(name)` — name stored raw, no length check; 64 pools/principal × unlimited + identities; each call also creates a `poolByPrincipal` entry and a `MarginEngine.open` + account; and **there is no close/delete path anywhere**, despite the cap message telling users + to close a pool. +- `_internet_identity_sign_in_start` — ungated, unthrottled, and each call costs the canister a + management-canister `raw_rand` while costing the caller nothing. + +The blanket fix the reporters suggest is good: Motoko's `inspect` accepts `arg : Blob`, so one +size check closes every oversized-payload variant at once. + +Credit: OhShii #5.1, #5.3, #5.5. + +--- + +## 2. Fix soon — integrity, fairness and value leaks + +| # | Finding | Credit | +|---|---|---| +| 2.1 | **Our own 31 Jul fix created an asymmetry.** `e66a27e` added the virtual offset to `stakeInsurance` only; `unstakeInsurance` has never been touched since genesis, so `redeem(mint(A)) > A` whenever share value > 1.0 — the normal state. Verification reproduced **+104,165 base units** exactly. | Menese #3.1 | +| 2.2 | `stakeInsurance`'s mint denominator omits `insuranceOwedUsd`, a receivable already earned by existing stakers. **Distinct root cause from 2.1** — fixing either alone leaves the other open. | OhShii #7.2 | +| 2.3 | Vault NAV haircuts `insuranceOwedUsd` but `withdrawLp` pays from gross holdings, flipping the sign of the safe loan-book asymmetry. The comment defending that asymmetry never mentions the arrears term. | OhShii #7.1 | +| 2.4 | `vaultPricesStale` tests a leg's **value** (`balance × price`) not its **balance**, so a leg priced at 0 is skipped by the guard while being marked at $0 in NAV. `createAmmPool` overwrites an existing pool with `refPrice = 0` unconditionally — plausibly an honest reconfiguration mistake. | OhShii #7.3 | +| 2.5 | Volume credit reaches the scorecard on **exactly one** settlement path. AMM-sweep fills, both cross-swap legs, and the expiry fallback never credit it — so honest makers filled by the AMM sweep earn nothing, and the under-counted exchange volume pins the level scale at its floor, which is what makes buying rank 2 cost ~$15. | OhShii #6.5 | +| 2.6 | `STAGED_CAP_PER_OWNER` keys on the raw principal while pools stage under **pool** principals, giving one account 65 × 32 = 2,080 slots — above `SHED_SOFT_STAGED`, so one account can raise the floor that excludes everyone else. | OhShii #6.6 | +| 2.7 | `mmQuoteStamp`/`mmOwnerStamp` are written at staging and cleared nowhere except `resetExchange`, so a staged-then-cancelled post-only order buys a free, renewable, market-agnostic L4 shield. Already listed as an unshipped guardrail in `docs/market-maker-program.md`. | OhShii #6.4 | +| 2.8 | The 2.5% breaker has no absolute anchor, and **XRC is unwired on every `#play` deploy** (`play_start.sh:131-134` runs `setXrcCanister "(null)"` — broader than the docs' cloud-engine framing), so there is no anchor *and* no alarm. `xrcSources` is written and never read. | OhShii #9.1 | +| 2.9 | `geptorFetchAndSweep` has no single-flight guard and `applyFreshAggregate` stamps continuation time, so a stale sample can overwrite a fresher one **and be stamped brand new** — defeating every gate keyed on `refPriceUpdatedNs` at once. `tickShipEvents` holds the only `finally` in 15k lines. | OhShii #9.2 | +| 2.10 | The archive's L2 shed fires on **queue depth alone** while its own comment says "if shipping is broken *and* the queue hit the cap". `_shipFailStreak` is maintained and consulted by the L1 roll on the very next line. ~21 minutes of backlog, or any stop/upgrade window that long, permanently destroys 50,000 events. **No adversary required.** | OhShii #9.4 | +| 2.11 | `verifyChain` reseeds `prev = null` per call, leaving one link per page unverified while returning `ok = true` — in the paged-audit flow its own docstring recommends. | Menese #3.2 | +| 2.12 | `trimOutliers` returns untrimmed below 3 samples while `PRICE_MIN_SOURCES = 2`. Real protection at n≥3 is the median's breakdown point, not the trim — which makes the n=2 case worse, not better. Bounded in practice by a 50 bps dispersion gate the reporters missed. | Menese #3.3 | +| 2.13 | `parseLeadingFloat` silently truncates scientific notation; `findAfter` is first-occurrence **by construction**, so every extractor is fragile, not just the three named. Latent for today's plain-decimal feeds. | Menese #3.4 | +| 2.14 | `processDeferredExpiry` releases an uncapped same-instant cohort through an O(K²) matcher. The codebase already shards for exactly this reason elsewhere. | Menese #2.1 | +| 2.15 | Bridge `claim` writes a permanent ledger row for an unvalidated asset **before** rejecting; the Bridge has no posture concept at all — and the same stub is wired into both production-facing deploy targets. | OhShii #5.2 | + +--- + +## 3. Documentation and readiness + +Docs asserting protections the code does not deliver — fix the text, or the code, but not +neither: + +- `getApiDoc()` and `docs.js` both claim other users' rows are filtered server-side / "never + leave the canister". `archiveExecute` deliberately serves every other user's deposits and + withdrawals. Under the transparency doctrine the *publicness* is defensible; the claims are + not. `main.mo` already contradicts itself on this ~600 lines below, and + `tests/test_archive_replay.sh` depends on the behaviour the docs deny. (OhShii #8.1) +- The owner gate on `getEventsForPrincipals` says it "closes the targeted vector", but + `getEventsRange`/`getDepositWithdrawals` are public and every row carries `user` **and** + `counterparty`. Doc-only would concede the gate is decorative. (OhShii #8.2) +- `bridge-and-cks-design.md` §11 states NNS sole-controllership in the **present tense**; + the live posture is a single operator principal. (OhShii #10.6d) +- The kill matrix references `claimPlayFunds` and `tests/test_play_claim.sh`. The method is + retired, `PLAY_BASKET` is gone, and **the test file does not exist** — yet + `pre-mainnet-checklist.md:49` lists it as a required verification step. (OhShii #10.6c) + +Code-side privacy gaps that don't conflict with the doctrine: the `capitalUsd` join +(OhShii #8.3), `order.id` de-anonymising partially-filled resting orders (#8.4a), and the +`_nameEntropySeeded` latch with no `postupgrade` — a shape `src/bridge/main.mo` already +documents and fixes (#8.4b). + +Production-readiness: three unbacked-credit endpoints lack the `IS_PRODUCTION` interlock every +sibling has, and `extMarketSwap` is reachable by a **non-controller** (OhShii #7.4); +`setTestEmailBinding` is gated on `IS_PRODUCTION` instead of `IS_DEV`, and the rebind escape +hatch its comment promises **does not exist as a method** (#10.6a); `injectHistoricalTrades` +has no posture gate (#10.6b); no build verification, module-hash check, or reproducible build, +and `npm install` rather than `npm ci` (#10.4). + +Operator-machine-only, but real: the `awk` program-text injection on the mainnet deploy path — +and because `/tmp` is sticky, a file planted by another local user **cannot** be removed or +overwritten by our own `rm -f`, so this is a persistent plant rather than a race (#10.1); +`cold_start.sh` still pattern-kills (`play_start.sh`'s own comment records a **fourth** fleet +kill on 2026-08-01) (#10.2); the keychain helper pre-authorises unsigned binaries against the +controller identity (#10.3); `deploy_to_engine.sh` passes a target `deploy.sh` doesn't +recognise, silently skipping `apply_anti_sybil_settings` and `apply_memory_settings` (#10.5). + +--- + +## 4. Found during verification — not reported by anyone + +1. **`lint-ratchet.sh`'s type-check gate cannot fail.** Step 1 omits the project's real build + flags, so its output fills with spurious `M0057` noise, and its detector is + `grep -qE ': error'` — but Motoko always emits `": type error [Mxxxx]"`, never a bare + `": error"`. It matches neither real errors nor the noise and prints `✓ type-check: ok` + unconditionally. Confirmed directly: `lint-ratchet.sh` exits **PASS** in the same tree where + `mops test` reports **14/15 files passing, 1 failing**. Menese's suggested fix (widen the + glob) is therefore *incomplete* — `tests/` is never fed to the idl step, and the M0155 grep + would ignore an M0151 anyway. This needs a real per-file type-check with a working pattern. + **The same missing flags had silently disabled a second gate**: the M0155 "hard zero" + ratchet reported 0 because moc aborted on M0057 before the analysis ran. With the flags + restored it immediately found **two real unguarded subtractions that had been in the tree + the whole time** (`main.mo` liq-price and slippage-impact paths, both now `SafeMath.subOrZero`). + So the repaired gate paid for itself on its first run. +2. **`openOrdersByUser` grows forever.** `OrderBook.mo`'s `removeFromOpenIndexes` never prunes + the outer `userKey` entry when a user's order set empties — unlike the price-level pruning + three lines above, which does. Every principal who has *ever* placed an order stays in the + map, and it feeds the uncapped `sweepStaleUserOrders` sweep. +3. **A second and third uncapped sweep.** Menese's "every other sweep is bounded" is wrong in a + direction that flatters us: `sweepStaleUserOrders` has no cap (they misattributed + `EVICT_MAX_PER_CALL`, which belongs to `evictOverCap` on the placement path), and `tickTier` + is only *partially* sharded — only its join-badge backfill uses `Shard.step`, while the + quoter/uptime sampling, a five-map level recompute, and lifetime-volume badge checks are + full scans every tick. +4. ~~**The integration suite runs against a replica the bot fleet is actively trading on.**~~ + **FIXED 2026-08-05, in this change set.** The finding was real: `deploy.sh local` → + `cold_start.sh` started local bots on *every* deploy and they kept trading while the suite + asserted exact balances and zero-order-book invariants, so a large share of its failures were + noise. `run_all.sh`'s guard could not stop them — the old `pkill -f simulate_trading.sh` + never matched the real process name (`trading_simulation.sh`). Measured at the time: stopping + the local supervisor by PID turned four of eight re-run failures green with no code change. + + The remedy was already half-built and unused. `scripts/lib/bots.sh` (committed in `868090f`) + records `.run/bots-.pid` on every start and stops a fleet by **ancestry** from that + pid — `mdx_kill_tree` walks `pgrep -P` and never matches on a name — but `cold_start.sh` and + `run_all.sh` were still pattern-killing beside it. This pass routes both through the + wrappers: `cold_start.sh` calls `stop_bots_local.sh` then `start_bots_local.sh`, so every + fleet it starts is recorded. `play_start.sh` already did. Verified live with three fleets + running concurrently (local, engine, subnet): each `.run/bots-*.pid` matched its supervisor + exactly, and the local stopper touched only the local tree. + + **`run_all.sh`'s half was inert until 2026-08-05, and this is the §7 pattern again.** The + guard was rewritten to call the stopper, so it read as repaired — but it resolved the path as + `$(dirname "$0")/../scripts/stop_bots_local.sh` while line 27 had already `cd`-ed into + `tests/`. `$0` is the invocation path, not the resolved one, so from the repo root the test + became `tests/tests/../scripts/…`, which does not exist; the `if` had no `else`, so the guard + silently did nothing. It worked when run from inside `tests/` and no-oped when run from the + repo root — which is how it is actually invoked. Caught by running the suite with the fleet + deliberately up and watching all 12 bots survive a run that claimed to have stopped them: + **20 red, against a baseline of 4.** Now resolved through the absolute `SCRIPT_DIR` computed + at the top of the file, gated on `-f` rather than `-x` (the stopper is invoked as + `bash `, which needs no executable bit, so `-x` let a `chmod` disable the guard), and + with an `else` that says the fleet was not stopped instead of staying quiet. + + The general lesson is the one #18 and #21 already charged us for: **a guard that fails + silently is worse than no guard**, because the suite still prints a total and that total gets + believed. Both halves of this one — the path and the `-x` — failed closed-mouthed. + + Two properties worth keeping. The stopper **does not fall back to a pattern search** when the + pid file is absent; it reports the unrecorded processes and stops, because guessing is what + killed the live subnet fleet three times. And `run_all.sh:100-115` prints any fleet tagged + `MDX_TARGET=` that it cannot account for, so an externally-started fleet is visible in the + suite's own output rather than silently skewing it. + +--- + +## 5. What the reporters got wrong + +Recorded for scoring, and because it calibrates how much to trust the rest. + +| Claim | Reality | +|---|---| +| "`mktemp` appears exactly once in the repo" (OhShii #10.1) | Also in `tests/run_all.sh` ×2, `tests/test_property_fuzz.sh`, `.claude/sync-ic-skills.sh` ×4. Their narrower "never in a deploy script" holds. | +| "the longest script on the deploy path" (#10.2) | `cold_start.sh` is third (448 lines vs `deploy.sh` 907, `seed.sh` 542), and two siblings share its weak `set` posture. | +| `withdraw` listed as an omitted **controller** capability (#10.6c) | It is a `requireAuth` user function, interlocked the opposite way (fails closed on `#production`). | +| "every other sweep in the codebase is bounded" (Menese #12) | Two counterexamples — see §4.3. | +| The `#3.3` n=3 contrast example (Menese) | `trimOutliers` doesn't exclude the outlier at n=3 either; the median's breakdown point does the work. Their conclusion still holds, their evidence doesn't. | +| A quoted in-code comment about staged cohorts (Menese #2.1) | Does not appear verbatim anywhere in the tree. The mechanism it describes is real. | +| `oracle-xrc-fallback-design.md` §3.4 "presents the breaker as bounding manipulation" (#9.1) | It's §3.3, and it is *more* candid than implied — it names the ratcheting risk explicitly, and §3.5 records that auto-halt was deliberately rejected for v1. | +| Vault worked example "+$112,392" (#7.3) | Their own formula gives $112,369.48. Immaterial; direction and magnitude correct. | + +Both teams also flagged confidence honestly where they had not established a precondition +(OhShii on the vault arrears window; Menese on the `capitalUsd` join's uniqueness), and +verification could not close those either. That restraint is worth more than the errors cost. + +--- + +## 6. Deliberately not done, and why + +Recorded so the gaps are visible rather than assumed closed. + +- **The `order.id` ↔ `fill.orderId` join is only half-closed.** `id` cannot be dropped from the + `order` projection: it is that entity's declared primary key and `Executor.mo:649` traps on a + hidden pk. The OQL half is already shut (the public `userEvent` projection carries no + `orderId`), but the archive's raw `getEventsRange` still returns the whole `#fill` variant, so + the join survives there. Closing it properly means per-kind redaction on the archive surface — + a larger change than this pass, tracked in §3. +- ~~**The async-dispatch question in §1.6 is still open.**~~ **SETTLED 2026-08-04, by the + compiler rather than the replica.** Moving the dispatch into a plain (non-`async`) helper made + moc reject `ignore tickLiquidations()` with `M0047, send capability required, but not + available`. Only a genuine message send needs that capability, so `tickLiquidations` schedules + a **separate message**: a trap inside it rolls back that message alone and the heartbeat + survives. Menese's *silent*-failure signature was the right one, and the two verification + agents who read it as an inline computation were wrong. This is also why the failure had to be + paced rather than merely counted — see §7.1. +- **Volume-credit consolidation (§2.5) is not implemented.** The four bypassing paths are + confirmed, but routing them all through one `creditTradeVolume` helper touches the settlement + path in four places and deserves its own change with its own fee-conservation tests, rather + than riding along with a security pass. +- **The Bridge posture gate went to `#production`, not `#dev` as first specified.** + `devSimulateDeposit` turned out to be the live `#play` on-ramp (the frontend Deposit page calls + it, and the play allowance flows through it), so a `#dev` gate would have deleted the only + on-ramp the committed posture has. The genuine hole was the uncapped `#production` posture, + where `playDepositCap()` returns null — that is what is now gated. +- **CSP is verified by configuration, not by response headers.** `security_policy: "standard"` + is set on the shipping asset config and the build was audited for what `standard` would break + (no `eval`, no WASM, no cross-origin fetches, no external fonts), but nothing has been + `curl -sD-`'d against a deployed asset canister yet. Do that on the next remote deploy. +- **`_minSourcesOverride` can still pin the source floor *below* the robustness floor.** It is + dev-gated and today's only caller raises it, so nothing is broken — but the hook can defeat + the n≥3 guarantee and should probably clamp. +- **The four red integration tests were baselined and are NOT caused by this change set.** + A true `HEAD` baseline was obtained by building `HEAD` plus an inert stub of the one method + this change set adds (`getLiquidationSweepHealth`, returning constants) — that makes the Candid + interface match, so the downgrade installs. (A plain downgrade is refused twice over: the + Candid gate rejects removing a method, and even past that the RTS refuses a + memory-incompatible downgrade, because this change set adds stable vars. `--mode reinstall` + is required.) Results on that baseline: + `test_release_priority` **2 failed, identical assertions**; `test_orderbook_priority` + **4 failed, identical**; `test_position_accounting` **1 failed, identical**. All three are + pre-existing. + `test_margin_heatmap` is a **venue-state artifact, not a code difference**: it never seeds and + never waits, so it assumes a warm venue. Run against a wiped replica it fails 5; seeding the + markets takes it to 2; the last two are `§8 history`, which only accumulates on the 30s + `HB_HEAT_NS` tick. It passed on the baseline solely because earlier tests in that run had + seeded the venue first. It should seed its own fixture or state its precondition. + +> **One genuine regression was found this way and fixed.** The first cut of the +> `_nameEntropySeeded` fix keyed the retry on `_nameEntropy == 0`, which re-enters that branch on +> **every heartbeat** whenever the randomness beacon is unavailable — and the `await` inside it +> splits the heartbeat message, so everything after it (requotes, heatmaps, shipping) stops +> running. It silently killed `tickHeatmaps` on a local replica. The shipped fix is the one the +> reporters originally proposed: a `system func postupgrade()` that clears the latch, so the +> retry happens once per upgrade and the hot path keeps no `await` at all. This is a good +> argument for taking a reporter's suggested remedy seriously rather than improvising a cleverer one. + +--- + +## 7. The second wave — issues #13–#21 (`andreij6`), filed 2026-08-02 + +Nine further reports landed *after* the triage above was written, from a third reviewer +working independently. Several are explicit corrections to conclusions recorded in §1–§6, and +they are right in every case checked so far. Verified against the working tree by symbol +lookup, same standard as above. + +Two of them make a structural point worth stating plainly, because it applies to how the +first wave was fixed rather than to any single finding: **a sibling fix landing next to a +defect can disguise it.** #18 predicted exactly that for `verifyChain` — the inbound +page-boundary seed (#3.2) is fixed and now carries a coverage-auditing test, so the function +reads as repaired while it still never consults the certified `chainHead`. #21 makes the same +shape of point about §4.1's lint gate. Both were correct. + +### 7.1 Fixed in this pass + +- **#15 item 1 — seize-loop exhaustion misclassified as insolvency.** §1.3 fixed the dust + trap and the missing rollback, but left the classifier reading only the resulting health. + The loop has two terminal states that both leave a user liquidatable: every collateral + walked and none could move the debt (genuinely insolvent), or the `MAX_SEIZE_ITERS` budget + ran out mid-walk with seizable collateral standing. The second reaching `#insolvent` means + `absorbBadDebt` writes off *every* remaining loan and socialises the residual — booking a + fully-collateralised position as a loss. The bound is a message-size bound and carries no + solvency meaning, so it is now recorded explicitly and the decision is a named pure + function, `Liquidator.classifySeizingPass`. Budget exhaustion reports `#liquidated` (a + partial close, which is what that variant already meant); the user stays in `loans` and the + next sweep resumes from the improved health. Pinned by `tests/Liquidator.test.mo`: the + classifier over all four terminal states, plus an invariant asserted across every + liquidation shape in the file — `#insolvent` may only be returned when nothing seizable is + left. + +- **#17 item 1 — the outlier trim could not reject at n=3 or n=4.** The band was + ±sigmaTrim·stddev of the whole set, outlier included, which is masking: at n=3 samples + (a, a, b) the 2σ band is ±1.1547·d while the outlier sits at exactly d, and the relation is + homogeneous in d, so a 0.1% outlier and a 100% outlier were both kept. At n=4 the band edge + lands exactly on the outlier and the inclusive keep test admitted it. Rejection began at + n=5, while a rate-limited source dropping in and out puts the fleet at 3–4 routinely — so + one venue ~1% off the cluster inflated `stddevBps` past the caller's 50bps gate and froze + the mark, the precise failure the trim exists to prevent. + + **The remedy proposed in §2.12 (and in issues #3.3/#9.1) does not fix this**, and OhShii + Labs withdrew it in the #17 thread: raising `PRICE_MIN_SOURCES` to 3 moves the failure from + "trim never runs" to "trim runs and mathematically cannot reject". That raise had already + landed here, which is why this was worth catching. The band now uses the **MAD** + (`PriceFeed.mad`), whose 50% breakdown point means no single reading can inflate it at any + magnitude, scaled by 1.4826 and by a Croux-Rousseeuw finite-sample factor — without the + latter the estimate runs ~50% low at n=3, and over-trimming is not a harmless direction + here, since every rejected sample costs a source against the `MIN_ROBUST_SOURCES` floor. + A band-width floor of `TRIM_BAND_FLOOR_BPS` (50bps, the caller's own dispersion tolerance) + handles MAD's implosion when more than half the samples share a value, which is the + ordinary case, not a contrived one. `aggregate` already measured dispersion on the surviving + cluster, so the second half of the reporter's fix needed no change once the trim worked. + + Two consequences recorded because they are behaviour changes, not just bug fixes. At n=4 + the mark no longer freezes on a lone ~1% outlier: the trim removes it and the surviving + three clear the gate. At n=3 it still does not move — but now because rejecting a sample + leaves only two survivors, which is below `MIN_ROBUST_SOURCES`, rather than because a + masked outlier blew the dispersion gate. Same outcome, honest reason, and consistent with + the module's existing rule that a mark may HOLD on two sources but not MOVE. The + 2026-07-12 live-incident regression (7 sources, one 1.5% high) still trims exactly one and + still clears the gate at ~14bps. + +- **#20 — six frontend money-scaling and display-integrity defects.** See §7.3. + +- **#12's consecutive-failure breaker (the third of the three specified remediations).** The + slice and the stamp-on-completion landed in the first pass; the breaker did not, and only + observability counters were added. That gap was worse than a no-op. Because `_lastLiqNs` is + now stamped **only** on completion — correct, since it is the health signal — a trapping batch + freezes it, so the heartbeat's `now - _lastLiqNs >= HB_LIQ_NS` predicate stayed true forever + and re-dispatched on **every beat** instead of every 30s: an unthrottled retry of a message + guaranteed to trap, billing for the instructions it burned before trapping each time. The + partial fix therefore turned a 30s failure loop into a per-heartbeat one. + + Cadence now comes from a dispatch stamp, which advances whether or not the pass survives, + while `_lastLiqNs` stays the completion-only health signal. `_liqFailStreak` counts dispatches + still incomplete when the next fell due, and past `LIQ_FAIL_BREAK_THRESHOLD` (3, mirroring the + archive's `SHIP_FAIL_ROLL_THRESHOLD`) retries back off geometrically to a 32-minute cap. + Backoff rather than a hard halt is deliberate: halting a solvency engine needs an operator to + notice, and if nobody does, liquidations never resume — backoff bounds the burn, keeps trying, + and recovers by itself on the first completed pass, which is the only thing that clears the + streak. One edge-triggered error log, `failStreak`/`backoffNs` on + `getLiquidationSweepHealth`, and `adminResetLiquidationBreaker` for an operator who has fixed + the cause. Pinned structurally in `tests/test_deploy_hygiene.sh` (6 assertions, verified to + fail on the pre-fix heartbeat) plus live assertions in `tests/test_audit_2026_08_fixes.sh`. + +### 7.2 Outstanding from the second wave + +Not addressed in this pass, listed so the gap is visible rather than assumed closed. Severity +order, most consequential first: + +- **#13 item 1** — `executeSwapCross` settles the sell leg, then returns `#err` from two + points below that settlement with no rollback, and every fill-capture hook sits under those + returns. The caller is told the swap failed while holding the proceeds (a client that + retries on `#err` double-sells), and because `refreshRolling24h` is the sole call site of + `emitFillEvents`, the settled fills are permanently absent from the hash-chained archive. +- **#18 finding 50** — `verifyChain` never anchors its recomputed tail to the certified + `chainHead`, so corruption of the newest event, or any consistent rewrite of a tail suffix, + verifies clean. This is the endpoint the sealed-season record points auditors at. +- **#15 item 2** — `settleNettedPair`'s dust path: `cash` floors to zero, `writeOffLoan` + rejects it and the result is `ignore`d, while the buyer's base debt is still forgiven. + Corrects §2's clearance of that function. +- **#16 finding 27** — `performLpDeposit` gates on the raw balance instead of available, so a + deposit can spend what a staged order reserved. +- **#14 findings 4/16/17** — order identity and time-in-force do not survive the sealed + release path: `ammSweepResting` re-rests under a fresh id without `linkStagedRelease` or + the user's `orderExpiry`, `cancelMyOrder` never consults `stagedReleasedAs`, and a staged + order whose expiry lapsed still executes as a taker. +- **#17 items 2 and 3** — the jump-breaker confirmation livelock, and USD/USDT venues pooled + into one median and one dispersion with the XRC anchor on the USDT side. +- **#16 finding 3** — the vault deposit fee is evaluated at the pre-deposit weight. +- **#13 item 3** — `swap()`/`quoteSwap()` never range-check `maxSlippage`, so an + out-of-range value traps instead of returning the structured `#err` four sibling endpoints + already return. +- **#19** — the scalability findings, including §4.3 above, which #19 re-derived + independently and correctly. None is a correctness defect; the value of the report is the + correction itself, since a sweep listed as "already bounded" never gets re-measured. +- **#21 part two** — three latent defects in the vendored OQL. They do not fire in the + deployed canister and activate only if the served-entity or secondary-index path is + adopted; they are a note on that future change. + +**#21 part one is already closed** — both legs of the lint gate were repaired in the §4.1 +work, and the M0155 count is now genuinely 0 rather than blind, because both real sites were +rewritten to `SafeMath.subOrZero`. **#13 item 2** (the `openPosition` VWAP wipe) was fixed +independently in `6f63107` after a live incident, before the report arrived. + +Neither wave has been acknowledged on the tracker, and `andreij6` is not yet scored in +[CONTRIBUTORS.md](../CONTRIBUTORS.md). diff --git a/docs/pre-mainnet-checklist.md b/docs/pre-mainnet-checklist.md index 988be50..8add723 100644 --- a/docs/pre-mainnet-checklist.md +++ b/docs/pre-mainnet-checklist.md @@ -46,7 +46,19 @@ sim behaviour while `DEPLOY_MODE = #dev`. - `debugInspectByUsername("")` as a controller → `found = false`, empty record. - `setTestBalance` no-ops; `getTestBalance` returns `0`. - - `claimPlayFunds()` → `#err(… deposit real assets via the Bridge)`. + - `setTestEmailBinding` **traps** (`requireDevHook`, `#dev`-only — it used + to be reachable on `#play`). + - `injectHistoricalTrades` → `#err("injectHistoricalTrades is not available + on #production")`. (On `#play` it is genesis-gated instead — accepted only + until the venue's first `enableAmm`; see docs/deployment-modes.md, "The + genesis window".) + - `fundArbitrageur`, `extMarketSwap`, and `donateToVault(_, false)` all + `#err` — the unbacked-credit interlock every sibling already had. + - ~~`claimPlayFunds()` → `#err(…)`~~ **REMOVED: the method is retired** (it is + absent from `main.mo` and `candid/backend.did`, `PLAY_BASKET` is gone, and + the `tests/test_play_claim.sh` this step used to cite does not exist). The + live on-ramp is the `PLAY_DEPOSIT_CAP_USD` reservation flow; verify that + instead. - [ ] **Wire the oracle fallback**: `setXrcCanister(opt principal "uf6dk-hyaaa-aaaaq-qaaaq-cai")` (the real XRC), then smoke: `adminRefreshXrcAnchors()` + `getXrcAnchors()` shows fresh anchors for diff --git a/icp.yaml b/icp.yaml index 840b21c..a627a19 100644 --- a/icp.yaml +++ b/icp.yaml @@ -89,7 +89,15 @@ canisters: configuration: dir: dist build: - - npm install + # `npm ci`, NOT `npm install`. ci installs EXACTLY what + # package-lock.json pins and fails if the lockfile and package.json + # disagree; install is free to resolve a newer semver-compatible + # version and to REWRITE the lockfile mid-build. Since this is the + # build that produces the assets served from multidex.ai, that + # difference is the whole supply-chain question: with `install` the + # committed lockfile is a suggestion, and the bytes shipped to users + # depend on when the deploy happened to run. + - npm ci - npm run build settings: environment_variables: diff --git a/ops/ai.multidex.bots.plist.template b/ops/ai.multidex.bots.plist.template index e477f7b..447675e 100644 --- a/ops/ai.multidex.bots.plist.template +++ b/ops/ai.multidex.bots.plist.template @@ -39,6 +39,12 @@ MDX_FOREGROUND 1 + + PATH + __HOME__/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin WorkingDirectory diff --git a/package-lock.json b/package-lock.json index 9b37a39..e22f694 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,19 @@ { "name": "uplands-dex", - "version": "0.1.0", + "version": "1.60.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "uplands-dex", - "version": "0.1.0", + "version": "1.60.0", "dependencies": { "@fontsource-variable/fraunces": "^5.2.9", "@fontsource-variable/inter": "^5.2.8", "@fontsource-variable/newsreader": "^5.2.10", "@icp-sdk/auth": "^7.0.0", "@icp-sdk/core": "^5.3.0", + "lightweight-charts": "^5.1.0", "minidenticons": "^4.2.1" }, "devDependencies": { @@ -1006,6 +1007,12 @@ "@esbuild/win32-x64": "0.25.12" } }, + "node_modules/fancy-canvas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fancy-canvas/-/fancy-canvas-2.1.0.tgz", + "integrity": "sha512-nifxXJ95JNLFR2NgRV4/MxVP45G9909wJTEKz5fg/TZS20JJZA6hfgRVh/bC9bwl2zBtBNcYPjiBE4njQHVBwQ==", + "license": "MIT" + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1045,6 +1052,15 @@ "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", "license": "ISC" }, + "node_modules/lightweight-charts": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/lightweight-charts/-/lightweight-charts-5.1.0.tgz", + "integrity": "sha512-jEAYR4ODYeyNZcWUigsoLTl52rbPmgXnvd5FLIv/ZoA/2sSDw63YKnef8n4yhzum7W926yHeFwlm7ididKb7YQ==", + "license": "Apache-2.0", + "dependencies": { + "fancy-canvas": "2.1.0" + } + }, "node_modules/minidenticons": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/minidenticons/-/minidenticons-4.2.1.tgz", @@ -1055,9 +1071,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", "dev": true, "funding": [ { @@ -1094,9 +1110,9 @@ } }, "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "dev": true, "funding": [ { @@ -1114,7 +1130,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, diff --git a/package.json b/package.json index d175a9e..045a885 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uplands-dex", - "version": "0.1.0", + "version": "1.60.0", "private": true, "scripts": { "dev": "vite --port 3000", @@ -13,6 +13,7 @@ "@fontsource-variable/newsreader": "^5.2.10", "@icp-sdk/auth": "^7.0.0", "@icp-sdk/core": "^5.3.0", + "lightweight-charts": "^5.1.0", "minidenticons": "^4.2.1" }, "devDependencies": { diff --git a/scripts/cold_start.sh b/scripts/cold_start.sh index cf9205e..d4d873b 100755 --- a/scripts/cold_start.sh +++ b/scripts/cold_start.sh @@ -59,7 +59,16 @@ # # Exit code: 0 on full success, non-zero if any deploy / seed / verify step fails. -set -o pipefail +# -e as well as -o pipefail. This script's job is a SEQUENCE — network, deploy, +# wire, top up, seed — where a silent failure early leaves a half-built +# exchange that looks deployed. Every site that is ALLOWED to fail was audited +# when -e was added and now says so explicitly with `|| true` (the ones that +# matter: the two `lsof` probes in zombie_masters, the DEPLOY_MODE grep, the +# xrc-/fuel-mock status probes, and the read-only diagnostic calls in §5 — all +# of them tolerate absence by design and every one of them is a PIPELINE, which +# under `pipefail` fails the whole assignment). `a && b` lists are exempt from +# -e by the shell's own rules and were left alone. +set -euo pipefail export PATH="$HOME/.local/bin:$PATH" GREEN='\033[0;32m' @@ -78,6 +87,13 @@ DO_SEED=true TOP_UP_AMOUNT="100t" SIMULATE="" while [ $# -gt 0 ]; do + # Under `set -u` a bare "$2" on a value-less flag aborts with an unbound- + # variable message that names nothing useful, and `shift 2` with one arg + # left aborts under -e. Name the mistake instead. + case "$1" in + --mode|--traders|--history-days|--top-up-amount) + [ $# -ge 2 ] || { echo "Flag $1 requires a value" >&2; exit 1; } ;; + esac case "$1" in --mode) MODE="$2"; shift 2 ;; --traders) TRADERS="$2"; shift 2 ;; @@ -109,6 +125,11 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_ROOT" +# Scratch-file locations (.run/, not fixed names under sticky /tmp) — see +# scripts/lib/runfiles.sh. +# shellcheck source=scripts/lib/runfiles.sh +. "$SCRIPT_DIR/lib/runfiles.sh" + log() { echo -e "${CYAN}▶${NC} $1"; } ok() { echo -e " ${GREEN}✓${NC} $1"; } warn() { echo -e " ${YELLOW}!${NC} $1"; } @@ -125,7 +146,11 @@ hdr() { echo -e "\n${YELLOW}═══ $1 ═══${NC}"; } # Refuse up front instead. Only guards runs that will actually SEED: # --no-seed (pure code update) and `empty` are posture-agnostic. if $DO_SEED; then - SRC_POSTURE=$(grep -oE 'DEPLOY_MODE : DeployMode = #[a-z]+' src/backend/main.mo 2>/dev/null | head -1 | sed 's/.*#//') + # `|| true`: an unreadable/unmatched literal is HANDLED below (the -n + # checks), but grep-finds-nothing exits 1 and `head -1` can SIGPIPE the + # producer — either way pipefail fails the assignment and -e would kill the + # script before the handling ran. + SRC_POSTURE=$(grep -oE 'DEPLOY_MODE : DeployMode = #[a-z]+' src/backend/main.mo 2>/dev/null | head -1 | sed 's/.*#//' || true) if [ "$MODE" = "play" ]; then if [ -n "$SRC_POSTURE" ] && [ "$SRC_POSTURE" != "play" ]; then err "--mode play needs DEPLOY_MODE = #play in src/backend/main.mo (found #$SRC_POSTURE)" @@ -162,15 +187,18 @@ hdr "Replica" # back to every pocket-ic on the machine. zombie_masters() { local owner repo pcwd - owner=$(lsof -nP -tiTCP:8000 -sTCP:LISTEN 2>/dev/null | head -1) # -t: bare PID + # Both lsof probes are ALLOWED to find nothing (no replica running / a pid + # that just exited): lsof exits non-zero and pipefail would fail the whole + # assignment under -e. Same trap deploy.sh documents at its own GATEWAY_PID. + owner=$(lsof -nP -tiTCP:8000 -sTCP:LISTEN 2>/dev/null | head -1 || true) # -t: bare PID repo=$(pwd -P) - for pid in $(pgrep -f "pocket-ic --ttl" 2>/dev/null); do - pcwd=$(lsof -a -p "$pid" -d cwd -Fn 2>/dev/null | sed -n 's/^n//p' | head -1) + for pid in $(pgrep -f "pocket-ic --ttl" 2>/dev/null || true); do + pcwd=$(lsof -a -p "$pid" -d cwd -Fn 2>/dev/null | sed -n 's/^n//p' | head -1 || true) [ "$pcwd" = "$repo" ] || continue # someone else's network — leave it alone [ "$pid" = "${owner:-}" ] || echo "$pid" done } -STRAYS=$(zombie_masters) +STRAYS=$(zombie_masters || true) if [ -n "$STRAYS" ]; then warn "zombie pocket-ic master(s): $(echo $STRAYS | tr '\n' ' ')— network is half-alive (calls/outcalls break); restarting it cleanly" icp network stop > /dev/null 2>&1 || true @@ -187,7 +215,7 @@ if curl -s --max-time 2 http://127.0.0.1:8000/api/v2/status > /dev/null 2>&1; th ok "replica already running at 127.0.0.1:8000 (single master owns the gateway)" else log "starting local replica (background)" - icp network start --background > /tmp/uplands-network-start.log 2>&1 || true + icp network start --background > "$MDX_NETWORK_START_LOG" 2>&1 || true # Give it a moment to come up. for i in $(seq 1 15); do if curl -s --max-time 1 http://127.0.0.1:8000/api/v2/status > /dev/null 2>&1; then @@ -197,7 +225,7 @@ else sleep 1 done if ! curl -s --max-time 2 http://127.0.0.1:8000/api/v2/status > /dev/null 2>&1; then - err "replica failed to start — check /tmp/uplands-network-start.log" + err "replica failed to start — check $MDX_NETWORK_START_LOG" exit 1 fi fi @@ -207,12 +235,24 @@ if $DO_DEPLOY; then hdr "Deploy" # Deploy requires controller identity. Anonymous is the controller in # local dev (see icp canister status → Controllers). - if ! icp identity default anonymous > /dev/null 2>&1; then + # + # PASS --identity EXPLICITLY on every icp invocation; never set the CLI's + # global default identity (mdex-process-safety §5). That default is GLOBAL + # state this script does not own: any other tool on the operator's machine + # (another checkout, an editor integration, an MCP connector holding its + # own principal) can move it between a default-flip and the command a line + # later. When that happens the deploy runs as a non-controller and every + # canister fails with IC0512 on update_settings, which reads like a + # permissions bug in the project rather than a race on shared state. The + # flag scopes the identity to the one command and cannot be raced. The + # probe below only VERIFIES the anonymous identity resolves — it writes + # no CLI state. + if ! icp identity principal --identity anonymous > /dev/null 2>&1; then err "anonymous identity missing; cannot deploy as controller" exit 1 fi log "icp deploy" - if ! icp deploy 2>&1 | tail -4; then + if ! icp deploy --identity anonymous 2>&1 | tail -20; then err "deploy failed" exit 1 fi @@ -226,8 +266,8 @@ if $DO_DEPLOY; then ALICE_PRINCIPAL=$(icp identity principal --identity alice 2>/dev/null || true) if [ -n "$ALICE_PRINCIPAL" ]; then log "adding alice as canister controller (admin scripts use --identity alice)" - icp canister settings update backend --add-controller "$ALICE_PRINCIPAL" --force > /dev/null 2>&1 || true - icp canister settings update frontend --add-controller "$ALICE_PRINCIPAL" --force > /dev/null 2>&1 || true + icp canister settings update backend --add-controller "$ALICE_PRINCIPAL" --force --identity anonymous > /dev/null 2>&1 || true + icp canister settings update frontend --add-controller "$ALICE_PRINCIPAL" --force --identity anonymous > /dev/null 2>&1 || true ok "alice promoted to controller ($ALICE_PRINCIPAL)" else warn "alice identity not found; admin scripts that use --identity alice will fail" @@ -245,7 +285,9 @@ if $DO_DEPLOY; then # and stores nothing, keeping the sim's oracle quiet; tests set rates # explicitly. On mainnet this wiring targets the REAL XRC # (uf6dk-hyaaa-aaaaq-qaaaq-cai) — see docs/pre-mainnet-checklist.md. - XRC_MOCK_ID=$(icp canister status xrc-mock --identity anonymous 2>/dev/null | awk -F': ' '/^Canister Id/{print $2; exit}' | tr -d '[:space:]') + # `|| true`: the mock is OPTIONAL — `icp canister status` exits non-zero when + # it isn't deployed, and the `if [ -n … ]` below is the intended handling. + XRC_MOCK_ID=$(icp canister status xrc-mock --identity anonymous 2>/dev/null | awk -F': ' '/^Canister Id/{print $2; exit}' | tr -d '[:space:]' || true) if [ -n "$XRC_MOCK_ID" ]; then icp canister call backend setXrcCanister "(opt principal \"$XRC_MOCK_ID\")" --identity anonymous > /dev/null 2>&1 || true ok "XRC mock wired (xrc-mock=$XRC_MOCK_ID)" @@ -254,13 +296,18 @@ if $DO_DEPLOY; then # (it plays BOTH the ICP ledger and the CMC, and really deposit_cycles the # DEX). On mainnet this wiring targets the real ledger + CMC — see # docs/pre-mainnet-checklist.md. - FUEL_MOCK_ID=$(icp canister status fuel-mock --identity anonymous 2>/dev/null | awk -F': ' '/^Canister Id/{print $2; exit}' | tr -d '[:space:]') + # `|| true`: optional mock, same reasoning as XRC_MOCK_ID above. + FUEL_MOCK_ID=$(icp canister status fuel-mock --identity anonymous 2>/dev/null | awk -F': ' '/^Canister Id/{print $2; exit}' | tr -d '[:space:]' || true) if [ -n "$FUEL_MOCK_ID" ]; then icp canister call backend setFuelRoute "(opt principal \"$FUEL_MOCK_ID\", opt principal \"$FUEL_MOCK_ID\")" --identity anonymous > /dev/null 2>&1 || true # The mock forwards REAL cycles on notify_top_up, so it needs a mintable - # balance well above any single deposit (tests burn ~1.5T a run). - icp canister top-up --amount 20t fuel-mock > /dev/null 2>&1 || true - ok "fuel route wired (fuel-mock=$FUEL_MOCK_ID as ledger+CMC, topped 20T)" + # balance well above any single deposit — and the ceiling deposit is an + # auto-fuel tranche, AUTO_FUEL_ICP_TRANCHE (100 ICP) × 10k cycles/e8s + # = 100T, not the ~1.5T a test burns. At 20T that tranche was refused + # (pre-2026-08-06: it TRAPPED and wedged the notify saga); 120T covers + # one full tranche + the mock's 1T refusal margin + a suite run's burns. + icp canister top-up --amount 120t fuel-mock --identity anonymous > /dev/null 2>&1 || true + ok "fuel route wired (fuel-mock=$FUEL_MOCK_ID as ledger+CMC, topped 120T)" fi # Google (Gemini) assistant key — from GOOGLE_API_KEY env (legacy alias # AI_API_KEY) or the git-ignored scripts/.google-api-key (never committed). @@ -334,36 +381,35 @@ fi # ── 4. Simulate (optional) ─────────────────────────────────────── if [ "$SIMULATE" = "yes" ]; then hdr "Simulation" - log "starting scripts/simulate_trading.sh in background" - # Kill any stale simulator first — including orphan ones from earlier - # sessions that survived a previous SIGTERM. Stacking simulators is - # bad: each carries its own LAST_PRICES anchor that drifts independently - # across a long session, and the older one's stale anchor causes the - # canister to print trades at wildly off-market prices (e.g. ICP - # at $1.27 against an oracle of $2.37). SIGKILL + verification stops - # this cold. - pkill -f "simulate_trading.sh" 2>/dev/null || true - # Give graceful shutdown a moment, then force any survivors. - sleep 1 - pkill -KILL -f "simulate_trading.sh" 2>/dev/null || true - # Wait up to 3s for processes to actually disappear. - for _ in 1 2 3 4 5 6; do - if ! pgrep -f "simulate_trading.sh" > /dev/null 2>&1; then break; fi - sleep 0.5 - done - if pgrep -f "simulate_trading.sh" > /dev/null 2>&1; then - warn "could not kill prior simulator(s); the new one will stack — check pgrep -fl simulate_trading.sh" - fi - nohup bash "$SCRIPT_DIR/simulate_trading.sh" --speed fast \ - > /tmp/uplands-sim.log 2>&1 & - disown - SIM_PID=$! - sleep 1 - if kill -0 "$SIM_PID" 2>/dev/null; then - ok "simulator running (pid $SIM_PID, log /tmp/uplands-sim.log)" + # ── NO PATTERN KILLS HERE. ── + # This block used to be: + # pkill -f "simulate_trading.sh" + # pkill -KILL -f "simulate_trading.sh" + # which is the exact pattern scripts/lib/targets.sh and + # scripts/stop_local_bots.sh document as having killed the LIVE multidex.ai + # fleet — 2026-07-23, 2026-07-28, and again on 2026-08-01. A pattern cannot + # tell a local simulator from one driving the subnet: the target used to + # live only in IC_ENV, and environment assignments are not part of argv, so + # `ps` shows the same string either way. play_start.sh was migrated to the + # PID-file architecture; this script was missed. + # + # Bots are now started and stopped through the same wrappers play_start.sh + # uses. mdx_bots_stop kills by ANCESTRY from the PID recorded at launch, so + # "stop the local bots" is provably scoped to the local fleet and CANNOT + # name a process on another target. The stale-simulator problem the old + # comment describes (two simulators, two independently-drifting LAST_PRICES + # anchors, trades printing far off the oracle mark) is solved better by the + # PID file: mdx_bots_start REFUSES to start a second fleet while the + # recorded one is alive, so they cannot stack in the first place. + log "stopping any recorded local fleet (PID-file scoped — never by pattern)" + bash "$SCRIPT_DIR/stop_bots_local.sh" || warn "stop_bots_local.sh reported a problem — continuing" + + log "starting the local trading fleet (scripts/start_bots_local.sh)" + if bash "$SCRIPT_DIR/start_bots_local.sh"; then + ok "local fleet running — stop it with: bash scripts/stop_bots_local.sh" else - warn "simulator exited immediately — check /tmp/uplands-sim.log" + warn "bot start failed — start it by hand with scripts/start_bots_local.sh" fi fi @@ -395,17 +441,22 @@ if [ "$MODE" = "full" ] || [ "$MODE" = "play" ]; then # first non-digit to drop the type suffix. Cutting at the first "_" # instead would keep only the leading digit group, and the before/ # after comparison would misread most live refreshes as "unchanged". + # Every call in this section is a READ-ONLY DIAGNOSTIC: a failure here means + # "we couldn't measure", not "the cold start failed", so each one keeps its + # own `|| true` and the reporting below handles the empty case. Without + # them, -e would turn an unreachable query into a failed bring-up AFTER the + # exchange was successfully deployed and seeded. MARKETS=(BTC-ICPUSD ETH-ICPUSD SOL-ICPUSD ICP-ICPUSD) PX_BEFORE=() for m in "${MARKETS[@]}"; do - POOL=$(icp canister call backend getAmmPool "(\"$m\")" 2>&1) + POOL=$(icp canister call backend getAmmPool "(\"$m\")" --identity anonymous 2>&1 || true) PX=$(echo "$POOL" | awk -F' *= *' '/refPrice / {gsub(/_/,"",$2); sub(/[^0-9].*/,"",$2); print $2; exit}') PX_BEFORE+=("$PX") done sleep 90 - STATS=$(icp canister call backend getPriceFeedStats '()' 2>&1) + STATS=$(icp canister call backend getPriceFeedStats '()' --identity anonymous 2>&1 || true) SUCCESS=$(echo "$STATS" | awk -F' *= *' '/successCount/ {gsub(/_/,"",$2); sub(/[^0-9].*/,"",$2); print $2}') FAILURES=$(echo "$STATS" | awk -F' *= *' '/failureCount/ {gsub(/_/,"",$2); sub(/[^0-9].*/,"",$2); print $2}') printf " refreshes: %s successes · %s failures\n" "${SUCCESS:-0}" "${FAILURES:-0}" @@ -414,7 +465,7 @@ if [ "$MODE" = "full" ] || [ "$MODE" = "play" ]; then for i in "${!MARKETS[@]}"; do m="${MARKETS[$i]}" before="${PX_BEFORE[$i]}" - POOL=$(icp canister call backend getAmmPool "(\"$m\")" 2>&1) + POOL=$(icp canister call backend getAmmPool "(\"$m\")" --identity anonymous 2>&1 || true) PX_AFTER=$(echo "$POOL" | awk -F' *= *' '/refPrice / {gsub(/_/,"",$2); sub(/[^0-9].*/,"",$2); print $2; exit}') if [ -n "$before" ] && [ -n "$PX_AFTER" ] && [ "$before" != "$PX_AFTER" ]; then printf " %-12s %s → %s ${GREEN}live${NC}\n" "$m" "$before" "$PX_AFTER" @@ -426,7 +477,7 @@ if [ "$MODE" = "full" ] || [ "$MODE" = "play" ]; then if [ "$LIVE" -eq 0 ] && [ "${SUCCESS:-0}" -lt 1 ]; then warn "price-feed timer appears dead — no refPrice moved in 90s and no successes recorded" - echo " → try directly: icp canister call backend fetchAndSetRefPrice '(\"BTC-ICPUSD\")'" + echo " → try directly: icp canister call backend fetchAndSetRefPrice '(\"BTC-ICPUSD\")' --identity anonymous" echo " → if that works, the outcall path is fine but the timer isn't firing (check postupgrade)" elif [ "$LIVE" -eq 0 ]; then # Prices very occasionally don't move in a 90s window — unusual @@ -444,5 +495,7 @@ echo " Frontend: http://frontend.local.localhost:8000/" echo " Backend : http://$(icp canister list 2>/dev/null | head -1).localhost:8000/" echo " Mode : $MODE" # play mode's bots are launched by play_start.sh, logging to the same file. -{ [ "$SIMULATE" = "yes" ] || [ "$MODE" = "play" ]; } && echo " Sim log : tail -f /tmp/uplands-sim.log" +# Both paths now launch the fleet through scripts/start_bots_local.sh, which +# logs to the PID-file run dir (scripts/lib/targets.sh: MDX_RUN_DIR). +{ [ "$SIMULATE" = "yes" ] || [ "$MODE" = "play" ]; } && echo " Bot log : tail -f $MDX_RUN_DIR/bots-local.log" echo "" diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 7759c0f..05a20ac 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -13,7 +13,7 @@ # cycles, play_start.sh seeding + bots). Already seeded → wasm # update only (cold_start --no-seed: build, upgrade, re-wire — # trading state, vault and insurance preserved). -# cloud an OpenCloud cloud engine. Guided: links the engine identity, +# engine an OpenCloud cloud engine (alias: `cloud`). Guided: links the engine identity, # remembers the subnet, deploys backend→bridge→frontend, wires, # injects AI keys, seeds (idempotent), offers bots. The ENGINE # holds the cycles and pays for compute, so the canister's own @@ -30,12 +30,15 @@ # Usage: # ./scripts/deploy.sh # asks for the target, then guides you # ./scripts/deploy.sh local # first time: seeded bring-up · else: update wasm -# ./scripts/deploy.sh cloud # cloud engine (guided; config remembered) +# ./scripts/deploy.sh engine # cloud engine (guided; config remembered; `cloud` = alias) # ./scripts/deploy.sh subnet # dedicated subnet (guided; config remembered) # ./scripts/deploy.sh frontend # FRONTEND-ONLY: rebuild + sync assets with the # # target's ids baked in (no backend/bridge/seed) # ./scripts/deploy.sh frontend --identity anonymous # legacy: plain `icp deploy` passthrough -# ./scripts/deploy.sh -e ic --subnet # legacy: plain mainnet deploy (no guidance) +# ./scripts/deploy.sh -e subnet --subnet # legacy: plain mainnet deploy (no guidance) +# # NOTE: use a DECLARED environment (`subnet`/`engine`) — +# # the shared `ic` mapping was retired (icp.yaml: it made +# # cross-stack deploys a one-file-swap accident). # # Flags: # --reconfigure re-ask the target identity + subnet (overwrites saved config) @@ -56,8 +59,8 @@ # insurance fund. It defaults to YES and only runs on a first deploy / after a # reset (skipped if the exchange already has AMM pools). # -# Non-interactive (CI): pre-set DEPLOY_TARGET=local|cloud|subnet (legacy -# CLOUD_ENGINE=true|false still means cloud|plain-passthrough), SEED / +# Non-interactive (CI): pre-set DEPLOY_TARGET=local|engine|subnet (legacy +# CLOUD_ENGINE=true|false still means engine|plain-passthrough), SEED / # RUN_BOTS = true|false, and pre-populate the conf files below. Subnet env # knobs: SUBNET_CYCLES (backend endowment at CREATE, default 5000t), and # TRUSTED_ATTRIBUTE_SIGNERS / FRONTEND_ORIGINS (anti-sybil canister env vars, @@ -75,14 +78,28 @@ set -euo pipefail cd "$(dirname "$0")/.." +# Scratch-file locations (.run/, not a fixed name under sticky /tmp) — see +# scripts/lib/runfiles.sh for why. Exported, so inject_history.sh and the +# other children this script spawns land on the same paths. +# shellcheck source=scripts/lib/runfiles.sh +. "$(cd "$(dirname "$0")" && pwd)/lib/runfiles.sh" + CONF="scripts/.cloud-engine.conf" -# Which icp ENVIRONMENT remote calls target. The two mainnet stacks are -# separate icp.yaml environments with their OWN canister-id mappings — +# $CE_ENV — which icp ENVIRONMENT remote calls target. The two mainnet stacks +# are separate icp.yaml environments with their OWN canister-id mappings — # `engine` (cloud engine) and `subnet` (the dedicated subnet serving # multidex.ai) — so a deploy can never land on the other stack's canisters -# (they also carry different controllers, but don't rely on that). Each -# branch sets this; `ic` remains only for the legacy plain passthrough. -CE_ENV="ic" +# (they also carry different controllers, but don't rely on that). The two +# remote branches set it (`subnet` / `cloud`); the `local` and `plain` targets +# exec out before anything reads it. +# +# DELIBERATELY NOT INITIALISED HERE. It used to default to `ic`, which was +# dead — every reachable path overwrote it — but a stale default is the wrong +# kind of safety net: `ic` is a bare network alias, not one of our declared +# environments, and it is exactly what the environment split moved away from — +# "the old shared `ic` mapping made that a one-file-swap accident" (icp.yaml). +# With no default, a branch that forgets to set CE_ENV dies on `set -u` naming +# the variable, instead of quietly aiming at an unmapped environment. TOKENS="ICPUSD BTC ETH SOL ICP" # market : indicative mid price, used only to lay a non-crossing seed ladder. MARKETS="BTC-ICPUSD:67500 ETH-ICPUSD:3500 SOL-ICPUSD:180 ICP-ICPUSD:12" @@ -146,16 +163,27 @@ save_subnet_conf() { } # Latest CoinGecko price for a market's base asset (written by inject_history.sh -# to /tmp/uplands-oracle-prices.txt during the history step), falling back to the +# to $MDX_ORACLE_PRICES during the history step), falling back to the # indicative price from MARKETS when history didn't run or that asset wasn't # fetched. Always emitted as a float literal (the icp CLI rejects a bare integer # for a Float arg). +# +# THE SNAPSHOT FILE IS INPUT, NOT CODE. Field 2 used to be spliced straight +# into an awk PROGRAM: `awk "BEGIN{printf \"%.4f\", $p}"`. awk has system(), so +# a value of `1; system("curl …|sh")` ran as the operator — on the mainnet +# deploy path. Two independent fixes, both kept: +# 1. VALIDATE — accept only a plain decimal; anything else falls back. +# 2. BIND with -v — `p` is then an awk DATA variable and can never be parsed +# as program text, whatever it contains. +# (`p+0` forces numeric context so an empty/odd value prints 0.0000 rather +# than tripping printf's type coercion.) seed_px() { local base="${1%-ICPUSD}" fallback="$2" p="" - [ -f /tmp/uplands-oracle-prices.txt ] && \ - p=$(awk -v a="$base" '$1==a{print $2; exit}' /tmp/uplands-oracle-prices.txt) + [ -f "$MDX_ORACLE_PRICES" ] && \ + p=$(awk -v a="$base" '$1==a{print $2; exit}' "$MDX_ORACLE_PRICES") + [[ "$p" =~ ^[0-9]+(\.[0-9]+)?$ ]] || p="" [ -z "$p" ] && p="$fallback" - awk "BEGIN{printf \"%.4f\", $p}" + awk -v p="$p" 'BEGIN{ printf "%.4f", p+0 }' } # Seed a freshly deployed (or reset) canister with the full play dataset. We @@ -187,7 +215,7 @@ cloud_seed() { warn "Seeding the play dataset: balances + price history + order book + AMM vault + insurance fund." warn "On mainnet every call is ~2s, so this takes several minutes — leave it running." - rm -f /tmp/uplands-oracle-prices.txt # don't let a stale history snapshot leak in + rm -f "$MDX_ORACLE_PRICES" # don't let a stale history snapshot leak in # 0) Posture check. Seeding mints test balances, which only #dev and #play # allow: the CONTROLLER-only setTestBalance survives on #play (the cloud @@ -220,10 +248,15 @@ cloud_seed() { # 2) Price history — CoinGecko-derived hourly trades for the chart backdrop. # inject_history.sh writes the latest price per asset to a temp file that # seed_px reads, so the book + AMM start where the history ends. + # GENESIS-GATED on #play (docs/deployment-modes.md): the canister accepts + # injection only until the venue's first enableAmm — fine on a fresh + # install (pools are enabled in step 4, after this), refused fast on an + # already-live venue (inject_history.sh probes the gate before fetching + # and names the refusal; seeding then continues on live prices). if [ "$hist_days" -gt 0 ]; then info "Injecting $hist_days days of price history (CoinGecko → injectHistoricalTrades)…" IC_ENV="$CE_ENV" bash scripts/inject_history.sh --days "$hist_days" --identity "$CE_IDENTITY" \ - || warn "History injection had failures (CoinGecko rate limits?) — the chart backdrop may be partial." + || warn "History injection had failures — see the lines above for the real cause (posture/genesis refusal or rate limits). The chart backdrop may be partial; seeding continues on live prices." fi # 2b) AMM pools + LIVE prices. Create + configure each pool, then price it @@ -242,8 +275,8 @@ cloud_seed() { px_e8=$(icp canister call backend getAmmPool "(\"$market\")" -e "$CE_ENV" --identity "$CE_IDENTITY" --query 2>/dev/null | grep -oE 'refPrice = [0-9_]+' | head -1 | tr -d '_' | awk '{print $3}') if [ -n "${px_e8:-}" ] && [ "$px_e8" != "0" ]; then px=$(awk -v p="$px_e8" 'BEGIN{ printf "%.4f", p/100000000 }') - { grep -v "^${market%%-*} " /tmp/uplands-oracle-prices.txt 2>/dev/null; echo "${market%%-*} $px"; } > /tmp/uplands-oracle-prices.txt.new \ - && mv /tmp/uplands-oracle-prices.txt.new /tmp/uplands-oracle-prices.txt + { grep -v "^${market%%-*} " "$MDX_ORACLE_PRICES" 2>/dev/null; echo "${market%%-*} $px"; } > "$MDX_ORACLE_PRICES.new" \ + && mv "$MDX_ORACLE_PRICES.new" "$MDX_ORACLE_PRICES" # Engine base leg for this market's vault deposit: half the per-market # TVL in base at the live price, +5% slack (role-sized — see step 1). base_fund=$(awk -v t="$tvl" -v p="$px" 'BEGIN{ printf "%.6f", t/p/2*1.05 }') @@ -265,7 +298,7 @@ cloud_seed() { icp canister call backend setTestBalance "(principal \"$tp\", \"ICPUSD\", $(e8 40000.0))" -e "$CE_ENV" --identity "$CE_IDENTITY" >/dev/null 2>&1 || true for m in $MARKETS; do px=$(seed_px "${m%%:*}" "${m##*:}") - qty=$(awk "BEGIN{printf \"%.6f\", 15000/$px}") + qty=$(awk -v p="$px" 'BEGIN{ printf "%.6f", 15000/p }') icp canister call backend setTestBalance "(principal \"$tp\", \"${m%%-*}\", $(e8 $qty))" -e "$CE_ENV" --identity "$CE_IDENTITY" >/dev/null 2>&1 || true done done @@ -278,9 +311,9 @@ cloud_seed() { market="${m%%:*}"; px=$(seed_px "$market" "${m##*:}") for t in "${traders[@]}"; do for k in 1 2; do - bid=$(awk "BEGIN{printf \"%.4f\", $px*(1-0.004*$k)}") - ask=$(awk "BEGIN{printf \"%.4f\", $px*(1+0.004*$k)}") - qty=$(awk "BEGIN{printf \"%.6f\", (2500*$k)/$px}") + bid=$(awk -v p="$px" -v k="$k" 'BEGIN{ printf "%.4f", p*(1-0.004*k) }') + ask=$(awk -v p="$px" -v k="$k" 'BEGIN{ printf "%.4f", p*(1+0.004*k) }') + qty=$(awk -v p="$px" -v k="$k" 'BEGIN{ printf "%.6f", (2500*k)/p }') icp canister call backend placeLimitOrder "(\"$market\", variant { buy }, $(e8 $bid), $(e8 $qty))" -e "$CE_ENV" --identity "$t" >/dev/null 2>&1 || true icp canister call backend placeLimitOrder "(\"$market\", variant { sell }, $(e8 $ask), $(e8 $qty))" -e "$CE_ENV" --identity "$t" >/dev/null 2>&1 || true done @@ -305,8 +338,8 @@ cloud_seed() { continue fi px=$(awk -v p="$px_e8" 'BEGIN{ printf "%.4f", p/100000000 }') - base=$(awk "BEGIN{printf \"%.6f\", $tvl/$px/2}") - quote=$(awk "BEGIN{printf \"%.2f\", $tvl/2}") + base=$(awk -v t="$tvl" -v p="$px" 'BEGIN{ printf "%.6f", t/p/2 }') + quote=$(awk -v t="$tvl" 'BEGIN{ printf "%.2f", t/2 }') seed_out=$(icp canister call backend seedAmmPool "(\"$market\", $(e8 $base):nat, $(e8 $quote):nat)" -e "$CE_ENV" --identity "$CE_IDENTITY" 2>&1) if echo "$seed_out" | grep -q "err"; then warn "$market — AMM vault seed FAILED: $(echo "$seed_out" | head -2 | tr '\n' ' ')" @@ -356,7 +389,7 @@ cloud_bots() { icp canister call backend setTestBalance "(principal \"$bp\", \"ICPUSD\", $(e8 40000.0))" -e "$CE_ENV" --identity "$CE_IDENTITY" >/dev/null 2>&1 || true for m in $MARKETS; do px=$(seed_px "${m%%:*}" "${m##*:}") - q=$(awk "BEGIN{printf \"%.6f\", 15000/$px}") + q=$(awk -v p="$px" 'BEGIN{ printf "%.6f", 15000/p }') icp canister call backend setTestBalance "(principal \"$bp\", \"${m%%-*}\", $(e8 $q))" -e "$CE_ENV" --identity "$CE_IDENTITY" >/dev/null 2>&1 || true done done @@ -376,15 +409,39 @@ cloud_bots() { command -v icp >/dev/null 2>&1 || die "icp CLI not found — install it (https://cli.internetcomputer.org), then re-run." # ── Decide target ──────────────────────────────────────────────── -# Positional keyword (local|cloud|subnet), --target=X, or DEPLOY_TARGET env; -# legacy CLOUD_ENGINE=true|false still maps to cloud|plain-passthrough. Bare +# Positional keyword (local|engine|subnet), --target=X, or DEPLOY_TARGET env; +# legacy CLOUD_ENGINE=true|false still maps to engine|plain-passthrough. Bare # canister names / icp flags with no target keep the historical behavior: a # plain `icp deploy` passthrough (no wiring, no seeding). +# +# ── ONE VOCABULARY ── +# `local | engine | subnet` — the same spelling scripts/lib/targets.sh uses +# (its table is the single source of truth for what a target IS), the same +# names icp.yaml gives its environments, and the same words in the +# deploy_to_.sh filenames. This script used to say `cloud` where +# everything else said `engine`, and that mismatch WAS the deploy_to_engine.sh +# bug: that wrapper execs `deploy.sh engine`, `engine` was not in this keyword +# list, so it fell through to PASS[] as if it were a canister name, TARGET +# resolved to "plain", and the deploy became a bare `icp deploy` passthrough +# that SKIPS apply_anti_sybil_settings and apply_memory_settings. The skipped +# frontend_origins re-stamp is what took multidex.ai sign-in down on +# 2026-07-11 — silently, because a passthrough deploy still succeeds. +# `cloud` stays accepted as an alias (docs, CI, muscle memory) but everything +# downstream compares against the canonical name only. +canonical_target() { + case "${1:-}" in + local) printf 'local' ;; + engine|cloud) printf 'engine' ;; + subnet) printf 'subnet' ;; + plain) printf 'plain' ;; # internal: the legacy `icp deploy` passthrough + *) return 1 ;; + esac +} TARGET="${DEPLOY_TARGET:-}" RECONFIGURE=false; SEED="${SEED:-}"; RUN_BOTS="${RUN_BOTS:-}"; PASS=(); SAW_FRONTEND=false while [ $# -gt 0 ]; do case "$1" in - local|cloud|subnet) if [ -z "$TARGET" ]; then TARGET="$1"; else PASS+=("$1"); fi ;; + local|cloud|engine|subnet) if [ -z "$TARGET" ]; then TARGET="$1"; else PASS+=("$1"); fi ;; frontend) SAW_FRONTEND=true ;; # ` frontend` = frontend-only; bare = legacy passthrough (restored below) --target) TARGET="${2:-}"; shift ;; --target=*) TARGET="${1#--target=}" ;; @@ -406,20 +463,28 @@ if $SAW_FRONTEND; then if [ -n "$TARGET" ]; then FRONTEND_ONLY=true; else PASS=("frontend" ${PASS[@]+"${PASS[@]}"}); fi fi if [ -z "$TARGET" ] && [ -n "${CLOUD_ENGINE:-}" ]; then - if truthy "$CLOUD_ENGINE"; then TARGET="cloud"; else TARGET="plain"; fi + if truthy "$CLOUD_ENGINE"; then TARGET="engine"; else TARGET="plain"; fi fi if [ -z "$TARGET" ]; then if [ ${#PASS[@]} -gt 0 ]; then TARGET="plain" else - case "$(ask 'Deploy target — [l]ocal replica, [c]loud engine, dedicated [s]ubnet:' 'l')" in - c*|C*) TARGET="cloud" ;; - s*|S*) TARGET="subnet" ;; - *) TARGET="local" ;; + case "$(ask 'Deploy target — [l]ocal replica, cloud [e]ngine, dedicated [s]ubnet:' 'l')" in + e*|E*|c*|C*) TARGET="engine" ;; + s*|S*) TARGET="subnet" ;; + *) TARGET="local" ;; esac fi fi +# HARD ERROR on anything we don't recognise — never a silent fall-through to +# the passthrough. A typo'd or renamed target must stop the deploy, not +# quietly downgrade it to a bare `icp deploy` that skips the safety steps. +# This also normalises the `cloud` alias, so every comparison below is against +# the canonical name. +TARGET_GIVEN="$TARGET" +TARGET="$(canonical_target "$TARGET_GIVEN")" || die "Unknown deploy target '$TARGET_GIVEN' — expected: local | engine | subnet ('cloud' is accepted as an alias for engine). Nothing was deployed." + # ── Posture gate: never ship #dev to a value-bearing target ────────── # # DEPLOY_MODE lives in src/backend/main.mo and is edited by hand — a local @@ -474,9 +539,9 @@ if $FRONTEND_ONLY; then local) FO_ENV="local"; FO_ID="anonymous" ;; - cloud) + engine) load_conf - [ -n "$CE_IDENTITY" ] || die "No cloud-engine conf — run a full 'deploy.sh cloud' once first." + [ -n "$CE_IDENTITY" ] || die "No cloud-engine conf — run a full 'deploy.sh engine' once first." icp identity principal --identity "$CE_IDENTITY" >/dev/null 2>&1 \ || die "Identity '$CE_IDENTITY' unusable (web delegation expired?) — run: icp identity reauth $CE_IDENTITY" FO_ENV="engine"; FO_ID="$CE_IDENTITY"; FO_SUBNET=(--subnet "$CE_SUBNET") @@ -488,7 +553,7 @@ if $FRONTEND_ONLY; then FO_ENV="subnet"; FO_ID="$SN_IDENTITY"; FO_SUBNET=(--subnet "$SN_SUBNET") export VITE_CLOUD_ENGINE=false ;; - *) die "frontend-only needs a real target: deploy.sh local|cloud|subnet frontend" ;; + *) die "frontend-only needs a real target: deploy.sh local|engine|subnet frontend" ;; esac if [ "$TARGET" != "local" ]; then # Ids from the target's own environment (same resolution `icp deploy` @@ -579,12 +644,14 @@ inject_ai_keys() { # i.e. never. Default 512 MiB of headroom; override with WASM_MEMORY_THRESHOLD. apply_memory_settings() { local limit thresh - limit="$(grep -oE 'WASM_MEMORY_LIMIT_BYTES : Nat = [0-9_]+' src/backend/main.mo 2>/dev/null | head -1 | grep -oE '[0-9_]+$' | tr -d '_')" + # The DECLARED ops-intent limit, applied at deploy. main.mo no longer + # carries a constant to grep: the canister reads its REAL limit back from + # canister_status and reports it via getCanisterInfo.wasmMemoryLimitBytes, + # so dashboard drift is structurally impossible — what remains here is the + # value ops WANTS applied (5.25 GiB; wasm64 hard wall is 6 GiB, keep margin). + # Override per-deploy with WASM_MEMORY_LIMIT. + limit="${WASM_MEMORY_LIMIT:-5637144576}" thresh="${WASM_MEMORY_THRESHOLD:-536870912}" - if [ -z "${limit:-}" ]; then - warn "Could not read WASM_MEMORY_LIMIT_BYTES from main.mo — leaving memory settings alone" - return 0 - fi if icp canister settings update backend -e "$CE_ENV" --identity "$CE_IDENTITY" \ --wasm-memory-limit "$limit" --wasm-memory-threshold "$thresh" 2>/dev/null; then ok "memory settings applied (limit $(awk -v b="$limit" 'BEGIN{printf "%.2f", b/1073741824}') GiB, lowmemory() at $(awk -v b="$thresh" 'BEGIN{printf "%.0f", b/1048576}') MiB free)" @@ -728,14 +795,18 @@ maybe_seed() { # ── plain: legacy `icp deploy` passthrough ─────────────────────── if [ "$TARGET" = "plain" ]; then - # Zombie-network check (LOCAL targets only — skip when deploying to ic). + # Zombie-network check (LOCAL targets only — skip when deploying remotely). # A launcher restart can leave two pocket-ic MASTERS (`--ttl` in argv); # the gateway still answers, but inter-canister calls and HTTPS outcalls # fail (oracles die, archive shipping stalls). Deploying onto that network # "works" and then misbehaves confusingly. This script must not wipe a # network it doesn't own, so it warns and defers to cold_start.sh, which # restarts the network cleanly. Seen 2026-07-06. - if ! printf '%s ' ${PASS[@]+"${PASS[@]}"} | grep -qE '(-e|--environment) *ic'; then + # The remote environments are the two declared mainnet stacks; anything else + # (`local`, `test`, or no -e at all) is the local replica and gets the check. + # (The old `-e ic` pattern matched NOTHING after the engine/subnet split, so + # this check misfired on every remote plain deploy.) + if ! printf '%s ' ${PASS[@]+"${PASS[@]}"} | grep -qE '(-e|--environment) *(subnet|engine)\b'; then # `|| true`: with no replica running lsof exits non-zero, and under # set -e the bare assignment would kill the script SILENTLY. GATEWAY_PID=$(lsof -nP -tiTCP:8000 -sTCP:LISTEN 2>/dev/null | head -1 || true) @@ -752,6 +823,15 @@ if [ "$TARGET" = "plain" ]; then export VITE_CLOUD_ENGINE=false # A plain deploy never seeds. The consistent first-install/update flow is # `./scripts/deploy.sh local|cloud|subnet`. + # Passthrough args are the caller's own; if they name no identity, icp + # falls back to the machine-global default — which other sessions and + # connectors move at will (mdex-process-safety §5). Warn, don't die: the + # legacy interface stays, but inheriting the default silently is how a + # deploy ends up signed by whatever identity the last tool left active. + case " ${PASS[*]:-} " in + *" --identity "*) ;; + *) warn "no --identity in the passthrough args — icp will sign as the machine-global default identity; pass --identity explicitly (mdex-process-safety §5)" ;; + esac info "Plain passthrough (cycles warning active; no wiring/seeding). Running: icp deploy ${PASS[*]:-}" exec icp deploy ${PASS[@]+"${PASS[@]}"} fi @@ -849,8 +929,12 @@ if [ "$TARGET" = "subnet" ]; then exit 0 fi -# ── cloud: OpenCloud engine — guided, all-in-one ───────────────── -[ "$TARGET" = "cloud" ] || die "Unknown deploy target '$TARGET' (expected local | cloud | subnet)." +# ── engine: OpenCloud cloud engine — guided, all-in-one ────────── +# Unreachable as a rejection now (canonical_target already died on anything +# else), but kept as a belt-and-braces assert: if a new target is ever added +# to canonical_target without a branch here, this fails loudly instead of +# running the engine flow against it. +[ "$TARGET" = "engine" ] || die "Internal error: target '$TARGET' has no deploy branch (expected local | engine | subnet)." export VITE_CLOUD_ENGINE=true CE_ENV="engine" # icp.yaml environment: the cloud-engine stack's own id mapping info "Cloud-engine mode: the engine pays for compute; the no-cycles warning will be suppressed." @@ -870,9 +954,14 @@ if [ -z "$CE_IDENTITY" ] || ! icp identity principal --identity "$CE_IDENTITY" > icp identity link web "$CE_IDENTITY" --auth "$CE_CONSOLE" \ || die "Identity link did not complete. First time? Enable 'CLI access' for your identity in the II settings, then re-run. (Skill: Step 1 / Pitfall 1.)" fi -icp identity default "$CE_IDENTITY" >/dev/null 2>&1 || die "Could not activate identity '$CE_IDENTITY'." +# No global "activation" step: setting the CLI's default identity here flips +# machine-shared state under every other session, checkout, and connector on +# this box (mdex-process-safety §5 — observed rug-pulling parallel +# workstreams, 2026-08-06). Every icp command in this flow names --identity +# explicitly, so all we need is proof the identity resolves. PRINCIPAL="$(icp identity principal --identity "$CE_IDENTITY" 2>/dev/null || true)" -[ -n "$PRINCIPAL" ] && ok "Deploying as '$CE_IDENTITY' ($PRINCIPAL)" +[ -n "$PRINCIPAL" ] || die "Identity '$CE_IDENTITY' unusable (web delegation expired?) — run: icp identity reauth $CE_IDENTITY" +ok "Deploying as '$CE_IDENTITY' ($PRINCIPAL)" # 2) Subnet id — required; remembered once given. if [ -z "$CE_SUBNET" ]; then diff --git a/scripts/gen-did.sh b/scripts/gen-did.sh index dd3da3d..a4793bd 100755 --- a/scripts/gen-did.sh +++ b/scripts/gen-did.sh @@ -16,15 +16,22 @@ set -euo pipefail cd "$(git rev-parse --show-toplevel)" -# `mops generate candid` extracts the interface directly from the Motoko -# source WITHOUT compiling wasm (needs ic-mops >= 2.19; the old -# `icp build` + copy-from-.mops/.build two-step did a full build just to -# read the interface). Output path is mops.toml's [canisters.backend].candid -# — an intermediate, gitignored; the published copy is what THIS script -# writes below. Verified byte-identical to the icp-build route (2026-07-31). -mops generate candid backend >/dev/null +# Extraction goes through scripts/lib/candid.sh, the SAME helper +# lint-ratchet.sh verifies this file with. That shared path is the point: a +# byte-equality gate whose writer and reader are different tools can drift +# between them, and this one did — the ratchet used `moc --idl` while this +# script used `mops generate candid`, which additionally needs ic-mops >= 2.19 +# and so failed outright on a machine running 2.13.2 while the check verifying +# its output ran fine. +# +# It writes src/backend/backend.did too: mops.toml declares that path as +# [canisters.backend].candid and `mops build` treats it as a compat-check +# INPUT, so a build fails without it. Gitignored — an intermediate; the +# PUBLISHED copy is what this script assembles below. +# shellcheck source=scripts/lib/candid.sh +. "$(dirname "$0")/lib/candid.sh" SRC="src/backend/backend.did" -[ -f "$SRC" ] || { echo "gen-did: $SRC not produced by 'mops generate candid backend'" >&2; exit 1; } +mdx_emit_candid "$SRC" || { echo "gen-did: could not extract the interface" >&2; exit 1; } mkdir -p candid { diff --git a/scripts/inject_history.sh b/scripts/inject_history.sh index 48e1c44..b337cc8 100755 --- a/scripts/inject_history.sh +++ b/scripts/inject_history.sh @@ -7,7 +7,7 @@ # multi-week backdrop before the simulation takes over. # # Writes a side-effect file that downstream scripts read: -# /tmp/uplands-oracle-prices.txt +# $MDX_ORACLE_PRICES (default: /.run/oracle-prices.txt) # Format: "SYMBOL PRICE" per line, e.g. # BTC 75412.3 # ETH 2305.01 @@ -16,6 +16,13 @@ # this so it doesn't have to hardcode stale "spot ~X" values for # AMM seeding — the AMM always starts at the latest oracle price. # +# POSTURE: injectHistoricalTrades is GENESIS-gated (docs/deployment-modes.md, +# "The genesis window"): #dev always · #play only until the venue's first +# enableAmm · #production never. This script probes the gate with an empty +# batch before fetching anything and exits 2 with the canister's own refusal +# when the window is closed — so run it BEFORE AMM enable in any #play +# bring-up (play_start.sh step 4 → 5, deploy.sh step 2 → 2b already do). +# # Usage: bash scripts/inject_history.sh [--days N] [--identity NAME] # IC_ENV=engine|subnet bash scripts/inject_history.sh --days 14 --identity # → inject into a deployed canister (e.g. a cloud engine) instead of local @@ -23,9 +30,18 @@ set -o pipefail export PATH="$HOME/.local/bin:$PATH" -# Target network. Unset/empty = the icp default (local replica). Set IC_ENV=ic -# to inject into a DEPLOYED canister (e.g. a cloud engine) — the calls below get -# `-e `. The --identity must be authorised for injectHistoricalTrades (a +# Scratch-file locations (.run/, not fixed names under sticky /tmp) — see +# scripts/lib/runfiles.sh. Honours an inherited MDX_RUN_DIR, so when a parent +# (deploy.sh / play_start.sh / seed.sh) spawns this script both agree on where +# the price snapshot lives. +# shellcheck source=scripts/lib/runfiles.sh +. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/runfiles.sh" + +# Target network. Unset/empty = the icp default (local replica). Set +# IC_ENV=engine|subnet to inject into a DEPLOYED canister — the calls below get +# `-e `. It must name a DECLARED icp.yaml environment: `ic` is a bare +# network alias, and none of our canisters are mapped under it. +# The --identity must be authorised for injectHistoricalTrades (a # canister controller on that network). ENV_FLAG="" [ -n "${IC_ENV:-}" ] && ENV_FLAG="-e ${IC_ENV}" @@ -69,7 +85,27 @@ QTY_ICP="50.0" INJECT_ASSETS="${INJECT_ASSETS:-BTC ETH SOL ICP}" want() { case " $INJECT_ASSETS " in *" $1 "*) return 0 ;; *) return 1 ;; esac; } -PRICES_FILE="/tmp/uplands-oracle-prices.txt" +# ── Posture probe — fail fast and HONESTLY when the canister refuses ── +# An empty batch is side-effect-free (zero records injected, stats untouched) +# but still runs the full gate chain: controller check + the genesis-window +# posture gate. When the gate refuses (#err on the current build, a dev-only +# trap on pre-window builds), every chunk of every asset would fail the same +# way — historically that surfaced as per-asset chunk errors which the +# callers then blamed on "CoinGecko rate limits", after burning a minute of +# fetches. Probe first; refusal here is a posture fact, not a data problem. +FIRST_ASSET=$(echo $INJECT_ASSETS | awk '{print $1}') +probe_out=$(echo y | icp canister call $ENV_FLAG backend injectHistoricalTrades \ + "(\"${FIRST_ASSET:-BTC}-ICPUSD\", vec {})" --identity "$IDENTITY" 2>&1) +probe_rc=$? +if [ "$probe_rc" -ne 0 ] || echo "$probe_out" | grep -q "err"; then + err "injectHistoricalTrades probe REFUSED before any data was fetched — not a CoinGecko problem. The call said:" + err " $(echo "$probe_out" | grep -m1 -iE 'genesis|dev-only|production|err|reject|trap' | sed 's/^[[:space:]]*//' | head -c 220)" + err " (posture gate — #dev: always · #play: only before the venue's first enableAmm · #production: never;" + err " a transport/identity failure prints its own error above instead)" + exit 2 +fi + +PRICES_FILE="$MDX_ORACLE_PRICES" # Upsert semantics: drop only the lines for assets injected THIS run — a # selective INJECT_ASSETS run must not wipe the other assets' snapshot # (play_start.sh and the simulator anchor from this file). @@ -180,7 +216,7 @@ inject_one() { # trade size never touches balances. local parse_out parse_out=$(python3 - "$raw" "$qty" "$asset" "$PRICES_FILE" <<'PY' -import json, sys, math, random +import json, sys, math, random, os raw = sys.argv[1]; base_qty = float(sys.argv[2]); asset = sys.argv[3]; prices_file = sys.argv[4] try: data = json.loads(raw) @@ -188,7 +224,8 @@ except Exception as e: # Keep the evidence: the head repr shows invisible bytes, and the full dump # answers "what did the API actually send" without re-reproducing. print(f"# parse error: {e}; len={len(raw)}; head={raw[:60]!r}", file=sys.stderr) - try: open(f"/tmp/uplands-inject-fail-{asset}.txt", "w").write(raw) + # Alongside the price snapshot (the .run/ dir), not a fixed /tmp name. + try: open(os.path.join(os.path.dirname(prices_file) or ".", f"inject-fail-{asset}.txt"), "w").write(raw) except Exception: pass sys.exit(1) prices = data.get("prices", []) @@ -273,9 +310,12 @@ PY batch+="$r;" batch_count=$(( batch_count + 1 )) if [ "$batch_count" -ge 250 ]; then - if ! out=$(echo y | icp canister call $ENV_FLAG backend injectHistoricalTrades \ - "(\"$market_id\", vec { $batch })" --identity "$IDENTITY" 2>&1); then - err " chunk injection failed: $(echo "$out" | grep -m1 -iE "error|reject|trap" | head -c 160)" + # Failure = a non-zero exit (reject/trap/transport) OR a typed #err in + # the reply — the genesis-window refusal is #err, which exits 0. + out=$(echo y | icp canister call $ENV_FLAG backend injectHistoricalTrades \ + "(\"$market_id\", vec { $batch })" --identity "$IDENTITY" 2>&1) + if [ $? -ne 0 ] || echo "$out" | grep -q "err"; then + err " chunk injection failed: $(echo "$out" | grep -m1 -iE "err|reject|trap|genesis" | head -c 200)" errors=$(( errors + 1 )) fi total=$(( total + batch_count )) @@ -283,9 +323,10 @@ PY fi done if [ -n "$batch" ]; then - if ! out=$(echo y | icp canister call $ENV_FLAG backend injectHistoricalTrades \ - "(\"$market_id\", vec { $batch })" --identity "$IDENTITY" 2>&1); then - err " final chunk failed: $(echo "$out" | grep -m1 -iE "error|reject|trap" | head -c 160)" + out=$(echo y | icp canister call $ENV_FLAG backend injectHistoricalTrades \ + "(\"$market_id\", vec { $batch })" --identity "$IDENTITY" 2>&1) + if [ $? -ne 0 ] || echo "$out" | grep -q "err"; then + err " final chunk failed: $(echo "$out" | grep -m1 -iE "err|reject|trap|genesis" | head -c 200)" errors=$(( errors + 1 )) fi total=$(( total + batch_count )) @@ -311,7 +352,9 @@ if [ -s "$PRICES_FILE" ]; then fi if [ "$FAILURES" -gt 0 ]; then - echo -e "${YELLOW}History injection completed with $FAILURES failures.${NC}" >&2 + # The posture probe passed at startup, so these are fetch-side failures + # (rate limits / bad data) — or the per-chunk lines above say otherwise. + echo -e "${YELLOW}History injection completed with $FAILURES failures (posture probe was OK — see per-asset errors above).${NC}" >&2 exit 1 fi echo -e "${GREEN}Historical backfill complete.${NC}" diff --git a/scripts/lib/bots.sh b/scripts/lib/bots.sh index 462f1b4..d00485e 100644 --- a/scripts/lib/bots.sh +++ b/scripts/lib/bots.sh @@ -11,7 +11,8 @@ set -uo pipefail # Which simulator drives each target. # # trading_simulation.sh supersedes the other two and is the one to run. The -# subnet deliberately still runs sim_trading.sh because that is what is +# Every target runs the strategy engine; the old spot-only sim_trading.sh +# retired at the Phase I -> II reset (2026-08-01). # driving multidex.ai's live volume today — switching the live fleet's engine # is a behavioural change that belongs in its own commit, not in a refactor # whose point is to stop things getting mixed up. When it is switched, this is @@ -19,7 +20,7 @@ set -uo pipefail mdx_engine_for() { case "$1" in local|engine) echo "trading_simulation.sh" ;; - subnet) echo "sim_trading.sh" ;; + subnet) echo "trading_simulation.sh" ;; esac } diff --git a/scripts/lib/candid.sh b/scripts/lib/candid.sh new file mode 100644 index 0000000..d373161 --- /dev/null +++ b/scripts/lib/candid.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# candid.sh — the ONE way this repo extracts the backend's Candid interface. +# +# SOURCE THIS, never execute it. +# +# There were two mechanisms doing the same job and they could disagree: +# gen-did.sh WROTE the published contract with `mops generate candid`, while +# lint-ratchet.sh VERIFIED it with `moc --idl`. Two tools that merely ought to +# produce identical bytes is a bad shape for a check whose entire purpose is +# byte-equality — and `mops generate candid` additionally needs ic-mops >= 2.19, +# which is why gen-did.sh could not run on a machine whose CLI was 2.13.2 while +# the ratchet verifying its output ran fine. +# +# Now generator and verifier both call mdx_emit_candid, so "fresh" means +# "identical to what the pinned moc emits", by construction rather than by +# coincidence. + +# mdx_emit_candid +# Extracts the backend interface to . Needs only the pinned moc +# (mops.toml [toolchain]) — no ic-mops version floor. +# +# --idl makes moc emit the interface as a side effect of codegen; the wasm is +# a throwaway. This is the same invocation lint-ratchet.sh has always used. +# The moc flags repeat lint-ratchet.sh's MOC_FLAGS on purpose — that copy is +# pinned by tests/test_deploy_hygiene.sh §5 and must not be "simplified" away. +mdx_emit_candid() { + local out="$1" + local root moc srcs tmp + root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" + moc="$(mops toolchain bin moc 2>/dev/null)" + [ -n "$moc" ] && [ -x "$moc" ] || { echo "mdx_emit_candid: moc unavailable — run 'mops install'" >&2; return 1; } + srcs="$(mops sources 2>/dev/null)" + + tmp="$(mktemp -d)" + # shellcheck disable=SC2086 — $srcs is a word list of --package flags + if ! (cd "$root" && "$moc" $srcs --default-persistent-actors --implicit-package=core --idl \ + -o "$tmp/backend.wasm" src/backend/main.mo) >/dev/null 2>&1 || [ ! -f "$tmp/backend.did" ]; then + rm -rf "$tmp" + echo "mdx_emit_candid: moc --idl failed to produce an interface" >&2 + return 1 + fi + mkdir -p "$(dirname "$out")" + mv "$tmp/backend.did" "$out" + rm -rf "$tmp" +} + +# mdx_published_body +# The committed contract with gen-did.sh's fixed 12-line header stripped, so +# it can be compared against raw moc output. Kept here rather than repeated as +# `tail -n +13` at each call site: the header length is a coupling between the +# writer and every reader, and it should live in one place. +MDX_CANDID_HEADER_LINES=12 +mdx_published_body() { + local out="$1" + local root; root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" + tail -n +$((MDX_CANDID_HEADER_LINES + 1)) "$root/candid/backend.did" > "$out" +} diff --git a/scripts/lib/runfiles.sh b/scripts/lib/runfiles.sh new file mode 100644 index 0000000..eb6da0e --- /dev/null +++ b/scripts/lib/runfiles.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# scripts/lib/runfiles.sh — where the deploy/seed scripts put scratch files. +# +# SOURCE THIS, never execute it. +# +# WHY THIS FILE EXISTS +# Every one of these paths used to be a FIXED NAME directly under /tmp: +# /tmp/uplands-oracle-prices.txt, /tmp/uplands-sim.log, and friends. Two +# properties of /tmp make that dangerous rather than merely untidy: +# +# · it is world-writable, so any local user can create the name first, and +# · it is STICKY (mode 1777), so a file another user owns CANNOT be removed +# or replaced by us — our own `rm -f` fails silently. A plant is therefore +# PERSISTENT, not a race we might win by running first. +# +# That mattered because deploy.sh read field 2 of the price snapshot straight +# into an awk PROGRAM TEXT, and awk has system(): arbitrary code execution as +# the operator, on the mainnet deploy path. The awk call sites are fixed +# separately (bind with -v, validate the value); this file removes the plant. +# +# .run/ lives inside the checkout (gitignored, see .gitignore), is owned by +# the operator and is not world-writable — so nobody else can pre-create these +# names and `rm -f` means what it says. +# +# WHY NOT `mktemp -d` PER RUN. Some of these files are the interface BETWEEN +# processes: inject_history.sh (a child process) writes the price snapshot, +# and deploy.sh / seed.sh / play_start.sh / simulate_trading.sh read it back. +# A per-run directory that only the parent knows about would silently break +# that hand-off — the readers would find nothing and fall back to stale +# hardcoded prices, which is exactly the failure play_start.sh's own comment +# describes (the sim walking a fresh AMM off its real price). One stable, +# operator-owned directory is the correct shape here; MDX_RUN_DIR is honoured +# from the environment and exported so a parent and its children always agree. + +MDX_ROOT="${MDX_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" +MDX_RUN_DIR="${MDX_RUN_DIR:-$MDX_ROOT/.run}" +mkdir -p "$MDX_RUN_DIR" 2>/dev/null || true +export MDX_ROOT MDX_RUN_DIR + +# ── Canonical scratch paths ──────────────────────────────────────── +# Latest per-asset price snapshot, "SYMBOL PRICE" per line. Written by +# inject_history.sh (and upserted by deploy.sh / play_start.sh); read by +# deploy.sh, seed.sh, play_start.sh and simulate_trading.sh. UNTRUSTED as far +# as the readers are concerned — validate before use, never interpolate into +# program text. +MDX_ORACLE_PRICES="${MDX_ORACLE_PRICES:-$MDX_RUN_DIR/oracle-prices.txt}" +export MDX_ORACLE_PRICES + +# Log/scratch files with a single writer and a single reader (the operator). +MDX_SIM_LOG="${MDX_SIM_LOG:-$MDX_RUN_DIR/sim.log}" +MDX_NETWORK_START_LOG="${MDX_NETWORK_START_LOG:-$MDX_RUN_DIR/network-start.log}" +MDX_PLAY_BUILD_LOG="${MDX_PLAY_BUILD_LOG:-$MDX_RUN_DIR/play-build.log}" +MDX_PLAY_FRONTEND_LOG="${MDX_PLAY_FRONTEND_LOG:-$MDX_RUN_DIR/play-frontend.log}" +export MDX_SIM_LOG MDX_NETWORK_START_LOG MDX_PLAY_BUILD_LOG MDX_PLAY_FRONTEND_LOG diff --git a/scripts/lint-ratchet.sh b/scripts/lint-ratchet.sh index eccd930..50bcabf 100755 --- a/scripts/lint-ratchet.sh +++ b/scripts/lint-ratchet.sh @@ -2,7 +2,27 @@ # Lint gate for the Motoko backend — run by the pre-push hook (.githooks/pre-push). # # Five checks, in order: -# 1. TYPE-CHECK — `moc --check` must succeed (catches real compile errors). +# 1. TYPE-CHECK — `moc --check` must succeed (catches real compile errors), +# for src/backend AND for every tests/*.mo. +# +# HOW THIS GATE WAS DEAD, 2026-08-02. Two independent faults, either of +# which alone would have been enough: +# · it ran moc WITHOUT the project's real build flags +# (--default-persistent-actors --implicit-package=core, see +# mops.toml), so moc could not resolve the project's own modules and +# emitted 143 spurious `M0057, unbound variable` errors on main.mo; +# · its detector was `grep -qE ': error'`, but moc always writes +# ": type error [Mxxxx]" or ": syntax error [Mxxxx]" — never a bare +# ": error". +# So it matched neither the real errors nor its own noise, and printed +# "✓ type-check: ok" unconditionally. It was confirmed PASSING in a tree +# where `mops test` reported a failing file. +# The fix is to use the build flags and to gate on moc's EXIT CODE — the +# candid step below always did this correctly and is the model. +# +# The same missing flags silently neutered check 3: without them moc +# aborts on M0057 before the Nat-subtraction analysis runs, so the +# "hard zero" M0155 ratchet counted zero because it counted NOTHING. # 2. lintoko — style lints (field punning, compound assignment, naming …) # must be ZERO on our hand-written code. Vendored src/backend/oql # is excluded (third-party). lintoko HAS no autofix, but these are @@ -56,19 +76,64 @@ fi # `git ls-files`) so brand-new, not-yet-tracked files are gated too — that is # exactly the code most likely to introduce a regression. OUR_FILES=$(find src/backend -name '*.mo' -not -path 'src/backend/oql/*' | sort) +# Every tests/*.mo — the `mops test` suite. Same reason to glob the filesystem. +TEST_FILES=$(find tests -name '*.mo' 2>/dev/null | sort) fail=0 -# 1) Type-check (main.mo transitively checks the whole backend). moc --check exits -# nonzero on warnings too, so gate on ERRORS only; surface other warnings as info. -TYPE_OUT=$("$MOC" $SRCS --check src/backend/main.mo 2>&1) -if printf '%s\n' "$TYPE_OUT" | grep -qE ': error'; then - echo "✗ type-check failed (moc --check):" >&2 - printf '%s\n' "$TYPE_OUT" | grep -E ': error' | head -20 >&2 +# The project's REAL build flags — keep in sync with mops.toml +# [canisters.backend].args. (The candid step below already passed these; the +# checks above it did not, which is the whole bug.) The backend's other args +# are output-shaping (--public-metadata, --max-stable-pages) and irrelevant to +# --check. +# Pinned by tests/test_deploy_hygiene.sh §5 — do not rename or inline. The +# same flag set is deliberately REPEATED in scripts/lib/candid.sh +# (mdx_emit_candid); "simplifying" either copy into the other breaks §5. +MOC_FLAGS=(--default-persistent-actors --implicit-package=core) + +# 1) Type-check the backend (main.mo transitively checks the whole thing). +# GATE ON THE EXIT CODE, never on a text pattern: moc --check exits 0 when +# there are only warnings and non-zero when there is any error, which is +# precisely the question being asked. Warnings are surfaced as info. +TYPE_OUT=$("$MOC" $SRCS "${MOC_FLAGS[@]}" --check src/backend/main.mo 2>&1) +TYPE_RC=$? +if [ "$TYPE_RC" -ne 0 ]; then + echo "✗ type-check FAILED — src/backend/main.mo (moc --check exit $TYPE_RC):" >&2 + printf '%s\n' "$TYPE_OUT" | grep -E 'error \[M' | head -20 >&2 fail=1 else WARN_N=$(printf '%s\n' "$TYPE_OUT" | grep -E ': warning \[M' | grep -v 'M0155' \ | grep -oE 'src/backend/[^:]+:[0-9]+' | grep -v '/oql/' | sort -u | wc -l | tr -d ' ') - echo "✓ type-check: ok (${WARN_N} non-M0155 moc warning(s) — run 'mops check' to see them)" + echo "✓ type-check: src/backend ok (${WARN_N} non-M0155 moc warning(s) — run 'mops check' to see them)" +fi + +# 1b) Type-check every tests/*.mo, ONE FILE AT A TIME. +# +# Widening the src/backend glob — the obvious fix — would NOT have covered +# this, for two reasons worth stating so nobody "simplifies" it back: +# · tests/ is never fed to the candid/idl step (check 4), so nothing else in +# this script ever compiles a test file; and +# · the M0155 loop (check 3) greps for that ONE warning code, so an M0151 — +# or a plain type error — in a test file would sail straight through it. +# Per-file is required because there is no root module that imports them all: +# each test is its own program, and a broken one is invisible until `mops +# test` fails in CI, long after the push this hook exists to block. +if [ -n "$TEST_FILES" ]; then + TEST_FAIL=0 + for f in $TEST_FILES; do + if ! T_OUT=$("$MOC" $SRCS "${MOC_FLAGS[@]}" --check "$f" 2>&1); then + echo "✗ type-check FAILED — $f:" >&2 + printf '%s\n' "$T_OUT" | grep -E 'error \[M' | head -10 | sed 's/^/ /' >&2 + TEST_FAIL=$(( TEST_FAIL + 1 )) + fi + done + if [ "$TEST_FAIL" -gt 0 ]; then + echo "✗ type-check: $TEST_FAIL test file(s) do not compile — 'mops test' cannot pass." >&2 + fail=1 + else + echo "✓ type-check: tests ok ($(printf '%s\n' "$TEST_FILES" | wc -l | tr -d ' ') file(s))" + fi +else + echo "lint-ratchet: no tests/*.mo found — skipping the unit-test type-check." >&2 fi # 2) lintoko — gate at ZERO on our code. @@ -87,7 +152,11 @@ else fi # 3) M0155 ratchet — count unique sites across our code. -M0155_N=$(for f in $OUR_FILES; do "$MOC" $SRCS --check "$f" 2>&1 | grep 'M0155'; done \ +# WITH the build flags, for the same reason as check 1: without them moc +# aborts on unresolved imports (M0057) before it ever runs the Nat-subtraction +# analysis, so this "hard zero gate" was reporting 0 because it was analysing +# nothing. Adding the flags is what makes the count real. +M0155_N=$(for f in $OUR_FILES; do "$MOC" $SRCS "${MOC_FLAGS[@]}" --check "$f" 2>&1 | grep 'M0155'; done \ | grep -oE 'src/backend/[^:]+\.mo:[0-9]+' | grep -v '/oql/' | sort -u | wc -l | tr -d ' ') if [ "$M0155_N" -gt "$M0155_BASELINE" ]; then echo "✗ M0155 (Nat-subtraction may-trap): $M0155_N > baseline $M0155_BASELINE — a new" >&2 @@ -101,36 +170,84 @@ else fi # 4) Published Candid contract — freshness (always) + subtype vs origin/main -# (when didc is installed). The full compile (--idl needs codegen, ~40s) only -# runs when checks 1-3 passed, so a broken tree fails fast above. +# (mops-first, didc fallback — and "could not check" now FAILS instead of +# silently passing). The full compile (--idl needs codegen, ~40s) only runs +# when checks 1-3 passed, so a broken tree fails fast above. if [ "$fail" -eq 0 ]; then DID_TMP=$(mktemp -d) trap 'rm -rf "$DID_TMP"' EXIT - if "$MOC" $SRCS --default-persistent-actors --implicit-package=core --idl \ - -o "$DID_TMP/new.wasm" src/backend/main.mo >/dev/null 2>&1 && [ -f "$DID_TMP/new.did" ]; then - # gen-did.sh prepends a fixed 12-line comment header; strip it for the diff. - if ! diff <(tail -n +13 candid/backend.did) "$DID_TMP/new.did" >/dev/null 2>&1; then + # Same helper gen-did.sh WRITES with, so "fresh" is byte-equality between one + # tool's output and itself rather than between two tools that ought to agree. + # shellcheck source=scripts/lib/candid.sh + . "$(dirname "$0")/lib/candid.sh" + if mdx_emit_candid "$DID_TMP/new.did"; then + mdx_published_body "$DID_TMP/published.did" + if ! diff -q "$DID_TMP/published.did" "$DID_TMP/new.did" >/dev/null 2>&1; then echo "✗ candid: candid/backend.did is STALE — the public surface changed without" >&2 echo " regenerating the published contract. Run scripts/gen-did.sh and commit." >&2 fail=1 else echo "✓ candid: committed contract matches the source" fi + # The subtype gate enforces "additive-only WITHIN a major apiVersion". The + # qualifier is load-bearing and the gate had no way to express it: a + # deliberate major bump is EXACTLY a non-subtype change, so without this + # escape hatch the gate blocks the one operation the stability policy + # explicitly permits. + # + # The hatch is the version itself, not a flag or an env var: bumping the + # MAJOR of MM_API_VERSION is a visible, reviewable, committed act that the + # policy already requires for a breaking change. You cannot take the hatch + # without also making the declaration — which is the point. + api_major_of() { # $1 = a git ref, or "" for the working tree + if [ -z "$1" ]; then grep -oE 'MM_API_VERSION : Text = "[0-9]+' src/backend/main.mo 2>/dev/null + else git show "$1:src/backend/main.mo" 2>/dev/null | grep -oE 'MM_API_VERSION : Text = "[0-9]+' + fi | head -1 | grep -oE '[0-9]+$' + } BASE_REF=$(git merge-base HEAD origin/main 2>/dev/null || true) - if command -v didc >/dev/null 2>&1 && [ -n "$BASE_REF" ] \ - && git show "$BASE_REF:candid/backend.did" 2>/dev/null | tail -n +13 > "$DID_TMP/base.did" \ + NEW_MAJOR=$(api_major_of "") + BASE_MAJOR=$(api_major_of "$BASE_REF") + if [ -n "$NEW_MAJOR" ] && [ -n "$BASE_MAJOR" ] && [ "$NEW_MAJOR" != "$BASE_MAJOR" ]; then + echo "✓ candid: apiVersion major $BASE_MAJOR → $NEW_MAJOR — breaking changes ALLOWED;" + echo " subtype gate skipped by declaration. Record it under 'Major bumps so far'" + echo " in candid/README.md if you have not already." + elif [ -n "$BASE_REF" ] \ + && git show "$BASE_REF:candid/backend.did" 2>/dev/null \ + | tail -n +$((MDX_CANDID_HEADER_LINES + 1)) > "$DID_TMP/base.did" \ && [ -s "$DID_TMP/base.did" ]; then - # didc check NEW OLD — NEW must be a subtype of OLD (old clients keep working). - if didc check "$DID_TMP/new.did" "$DID_TMP/base.did" > "$DID_TMP/didc.out" 2>&1; then - echo "✓ candid: backward-compatible with origin/main (didc subtype check)" + # `mops check-candid NEW ORIGINAL` — NEW must be a subtype of ORIGINAL + # (old clients keep working). Same semantics as `didc check`, but mops is + # already a hard requirement of this repo, so the gate no longer hinges + # on didc being installed — it used to print "skipping" and PASS without + # it, and a breaking-change gate that is off by default is not a gate. + # (The other half of the old inertness — the pre-push hook not being + # installed — is a hook/CI problem, not this script's.) didc stays as a + # fallback for environments that have it but not mops. + if command -v mops >/dev/null 2>&1; then + CANDID_CHECK=(mops check-candid) + elif command -v didc >/dev/null 2>&1; then + CANDID_CHECK=(didc check) + else + CANDID_CHECK=() + fi + if [ ${#CANDID_CHECK[@]} -eq 0 ]; then + echo "✗ candid: neither mops nor didc available — cannot run the breaking-change gate." >&2 + echo " Refusing to treat 'could not check' as 'compatible'." >&2 + fail=1 + elif "${CANDID_CHECK[@]}" "$DID_TMP/new.did" "$DID_TMP/base.did" > "$DID_TMP/didc.out" 2>&1; then + echo "✓ candid: backward-compatible with origin/main (${CANDID_CHECK[0]} subtype check)" else echo "✗ candid: BREAKING interface change vs origin/main — the published API is" >&2 echo " additive-only within a major apiVersion (see candid/backend.did header):" >&2 head -10 "$DID_TMP/didc.out" >&2 + echo " If this removal is INTENDED, bump the major of MM_API_VERSION and add a" >&2 + echo " 'Major bumps so far' entry in candid/README.md — that is the sanctioned route." >&2 fail=1 fi else - echo "lint-ratchet: didc not installed — skipping the subtype (breaking-change) gate." >&2 + echo "✗ candid: could not read origin/main's contract — breaking-change gate did not run." >&2 + echo " Fetch origin (git fetch origin main) so there is a baseline to compare against." >&2 + fail=1 fi else echo "✗ candid: could not regenerate the interface (moc --idl failed)" >&2 diff --git a/scripts/play_start.sh b/scripts/play_start.sh index 6e80c9e..ff98edd 100755 --- a/scripts/play_start.sh +++ b/scripts/play_start.sh @@ -10,7 +10,13 @@ # - setTestBalance controller-only operator funding (bots, ladder # makers, the AMM LP) — recorded in extNetFlow so # the profit leaderboard stays honest -# - injectHistoricalTrades chart backdrop (via inject_history.sh) +# - injectHistoricalTrades chart backdrop (via inject_history.sh) — +# GENESIS-GATED on #play: the canister accepts it +# only until the venue's first enableAmm, #err +# after (one-way; reinstall re-arms, season resets +# do not). Works here because step 4 (history) +# runs on the fresh reinstall, before step 5's +# enableAmm. See docs/deployment-modes.md. # - createAmmPool / setAmmConfig / seedAmmPool / setAmmSkewConfig / # enableAmm / setAmmAutoInventory controller AMM lifecycle # - fetchAndSetRefPrice REAL prices; setAmmRefPrice returns #err on #play @@ -40,6 +46,12 @@ set -uo pipefail cd "$(dirname "$0")/.." + +# Scratch-file locations (.run/, not fixed names under sticky /tmp) — see +# scripts/lib/runfiles.sh. Exported, so inject_history.sh lands on the same +# price-snapshot path this script then reads back. +# shellcheck source=scripts/lib/runfiles.sh +. "$(cd "$(dirname "$0")" && pwd)/lib/runfiles.sh" export PATH="$HOME/.local/bin:$PATH" GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; CYAN='\033[0;36m'; NC='\033[0m' @@ -97,7 +109,7 @@ ok "canisters present (backend=$BACKEND_ID bridge=$BRIDGE_ID)" # ── 1. Build + reinstall (fresh play state) ─────────────────────── hdr "Build + reinstall" log "icp build (backend + bridge wasm)" -icp build > /tmp/uplands-play-build.log 2>&1 || die "build failed — see /tmp/uplands-play-build.log" +icp build > "$MDX_PLAY_BUILD_LOG" 2>&1 || die "build failed — see $MDX_PLAY_BUILD_LOG" ok "built" log "reinstalling backend (wipes ALL exchange state)" @@ -122,7 +134,7 @@ ok "Bridge ↔ DEX auto-wired via canister env vars" FUEL_MOCK_ID=$(icp canister status fuel-mock --identity anonymous 2>/dev/null | awk -F': ' '/^Canister Id/{print $2; exit}' | tr -d '[:space:]') if [ -n "$FUEL_MOCK_ID" ]; then adm setFuelRoute "(opt principal \"$FUEL_MOCK_ID\", opt principal \"$FUEL_MOCK_ID\")" > /dev/null || true - icp canister top-up --amount 20t fuel-mock > /dev/null 2>&1 || true + icp canister top-up --amount 20t fuel-mock --identity anonymous > /dev/null 2>&1 || true ok "fuel route wired (fuel-mock as ledger+CMC, topped 20T)" else warn "fuel-mock not found — auto-fuel Stage 2 unwired" @@ -181,12 +193,16 @@ adm setTestBalance "(principal \"$LP_PRINCIPAL\", \"ICPUSD\", $(e8 $(( TVL_TOTAL ok "LP quote leg funded (\$$(( TVL_TOTAL / 2 + 50000 )) ICPUSD)" # ── 4. Price history (chart backdrop) ───────────────────────────── +# ORDER MATTERS: injectHistoricalTrades is genesis-gated on #play — accepted +# only until the FIRST enableAmm of the install, then #err forever (a season +# reset does not re-arm it; the reinstall in step 1 is what re-armed it for +# this run). Keep this step ahead of step 5's enableAmm. hdr "Price history" -rm -f /tmp/uplands-oracle-prices.txt +rm -f "$MDX_ORACLE_PRICES" if [ "$HIST_DAYS" -gt 0 ]; then log "injecting $HIST_DAYS days of CoinGecko history (this takes a minute)…" bash scripts/inject_history.sh --days "$HIST_DAYS" --identity anonymous \ - || warn "history injection had failures (CoinGecko rate limits?) — chart backdrop may be partial" + || warn "history injection had failures — see inject_history.sh's lines above for the real cause (it probes the posture gate first, so a refusal is named as such, not blamed on rate limits); chart backdrop may be partial" else warn "HISTORY_DAYS=0 — skipping the chart backdrop" fi @@ -230,8 +246,8 @@ for market in $MARKETS; do # has lastPrice=0 and no snapshot line — the sim would then trade at the # stale default and walk the fresh AMM off its real price (seen with BTC: # default 78000 vs live 63200). Upsert the live price for every market. - { grep -v "^$base_tok " /tmp/uplands-oracle-prices.txt 2>/dev/null; echo "$base_tok $px"; } > /tmp/uplands-oracle-prices.txt.new \ - && mv /tmp/uplands-oracle-prices.txt.new /tmp/uplands-oracle-prices.txt + { grep -v "^$base_tok " "$MDX_ORACLE_PRICES" 2>/dev/null; echo "$base_tok $px"; } > "$MDX_ORACLE_PRICES.new" \ + && mv "$MDX_ORACLE_PRICES.new" "$MDX_ORACLE_PRICES" # Makers' base inventory: $15k of this market's base each (the $100k # parity mix) — sized here because the quantity needs the live price. @@ -328,8 +344,8 @@ bash scripts/start_bots_local.sh || warn "bot start failed — run scripts/start # ── 8. Frontend ─────────────────────────────────────────────────── hdr "Frontend" log "icp deploy frontend (build + certified asset sync)" -icp deploy frontend --identity anonymous > /tmp/uplands-play-frontend.log 2>&1 \ - || warn "frontend deploy failed — see /tmp/uplands-play-frontend.log" +icp deploy frontend --identity anonymous > "$MDX_PLAY_FRONTEND_LOG" 2>&1 \ + || warn "frontend deploy failed — see $MDX_PLAY_FRONTEND_LOG" ok "frontend deployed" # ── 9. Summary ──────────────────────────────────────────────────── diff --git a/scripts/seed.sh b/scripts/seed.sh index 5797fbc..920834e 100755 --- a/scripts/seed.sh +++ b/scripts/seed.sh @@ -56,7 +56,18 @@ err() { echo -e " ${RED}✗${NC} $1" >&2; } # Pipe "y" into icp canister call so the interactive confirm is # auto-accepted when candid can't be inferred. -call() { echo "y" | icp canister call backend "$@" 2>&1; } +# Every call site MUST name its identity: the CLI's global default identity +# is machine-shared mutable state that other sessions and connectors move at +# will (mdex-process-safety §5), so an identity-less call here would sign as +# whoever happens to be active. All fixtures seed as a KNOWN identity — +# refuse loudly rather than half-seed as the wrong principal. +call() { + case " $* " in + *" --identity "*) ;; + *) err "call() without --identity (mdex-process-safety §5): $*"; return 1 ;; + esac + echo "y" | icp canister call backend "$@" 2>&1 +} # Integer-money: human decimal -> integer base units (10^8 / e8s). Money # args (balances, prices, qtys, amounts) are now Nat base units on the wire. @@ -64,6 +75,11 @@ e8() { awk -v x="$1" 'BEGIN{ printf "%.0f", x*100000000 }'; } SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# Scratch-file locations (.run/, not fixed names under sticky /tmp) — see +# scripts/lib/runfiles.sh. +# shellcheck source=scripts/lib/runfiles.sh +. "$SCRIPT_DIR/lib/runfiles.sh" + # ── Posture guard ──────────────────────────────────────────────── # Every fixture below leans on #dev-only surfaces (setAmmRefPrice, test # overrides). On #play those #err — and because this script discards call @@ -119,7 +135,7 @@ generate_phantom_principals() { # ── Wait for any previous icp calls to settle ──────────────────── # Sanity-ping the backend; aborts early if the replica isn't responding. -if ! icp canister call backend getMarkets '()' > /dev/null 2>&1; then +if ! icp canister call backend getMarkets '()' --identity anonymous > /dev/null 2>&1; then # Maybe ensureInit hasn't run yet; try an update call to trigger it. alice=$(ensure_identity alice) if ! call resetExchange --identity alice > /dev/null 2>&1; then @@ -234,7 +250,7 @@ seed_load() { # (the bulk endpoint expects Principal values, not text). Each run # gets FRESH principals — fine, because load mode resets the exchange # and re-funds them; nothing references the previous run's principals. - local tmpfile=/tmp/uplands-loadtrader-principals.txt + local tmpfile="$MDX_RUN_DIR/loadtrader-principals.txt" generate_phantom_principals "$TRADERS" "$tmpfile" local count count=$(wc -l < "$tmpfile" | tr -d ' ') @@ -425,8 +441,8 @@ seed_full() { ok "5 traders funded" # Step 2: fetch oracle prices + historical candles. Writes - # /tmp/uplands-oracle-prices.txt which steps 3 & 4 read. - local oracle_file=/tmp/uplands-oracle-prices.txt + # $MDX_ORACLE_PRICES which steps 3 & 4 read. + local oracle_file="$MDX_ORACLE_PRICES" rm -f "$oracle_file" if [ "$HISTORY_DAYS" -gt 0 ]; then log "injecting $HISTORY_DAYS days of oracle-derived price history" diff --git a/scripts/seed_exchange.sh b/scripts/seed_exchange.sh index ff9ac62..f02861d 100755 --- a/scripts/seed_exchange.sh +++ b/scripts/seed_exchange.sh @@ -23,7 +23,17 @@ log() { echo -e "${CYAN}▶${NC} $1"; } ok() { echo -e " ${GREEN}✓${NC} $1"; } head() { echo -e "\n${YELLOW}═══ $1 ═══${NC}"; } -call() { echo "y" | icp canister call backend "$@" 2>&1; } +# Every call site MUST name its identity: the CLI's global default identity +# is machine-shared mutable state that other sessions and connectors move +# at will (mdex-process-safety §5) — an identity-less call would sign as +# whoever happens to be active. Refuse loudly rather than mis-seed. +call() { + case " $* " in + *" --identity "*) ;; + *) echo " ✗ call() without --identity (mdex-process-safety §5): $*" >&2; return 1 ;; + esac + echo "y" | icp canister call backend "$@" 2>&1 +} # ── DEPRECATED ─────────────────────────────────────────────────── # Superseded by scripts/seed.sh (the unified seed dispatcher). This script's diff --git a/scripts/set_ai_key.sh b/scripts/set_ai_key.sh index 7ecdebb..0d3bb80 100644 --- a/scripts/set_ai_key.sh +++ b/scripts/set_ai_key.sh @@ -37,6 +37,16 @@ while [ $# -gt 0 ]; do esac done +# -e/--identity thread through PASS. If the caller named no identity, pin +# anonymous rather than inherit the CLI's machine-global default, which +# other sessions and connectors move at will (mdex-process-safety §5). +# Locally anonymous IS the controller; against engine/subnet an anonymous +# call fails loudly as a non-controller instead of racing the default. +case " ${PASS[*]:-} " in + *" --identity "*) ;; + *) PASS+=(--identity anonymous) ;; +esac + case "$PROVIDER" in google) METHOD="setGoogleApiKey"; DOTFILE="$(dirname "$0")/.google-api-key" ;; anthropic) METHOD="setAnthropicApiKey"; DOTFILE="$(dirname "$0")/.anthropic-api-key" ;; @@ -51,7 +61,7 @@ if [ -z "$KEY" ]; then read -rs KEY; echo >&2 fi -echo "set_ai_key: calling $METHOD on '$BACKEND' (${PASS[*]:-local defaults})…" +echo "set_ai_key: calling $METHOD on '$BACKEND' (${PASS[*]})…" icp canister call ${PASS[@]+"${PASS[@]}"} "$BACKEND" "$METHOD" "(\"$KEY\")" >/dev/null # Verify via the public gate the frontend itself uses. diff --git a/scripts/sim_trading.sh b/scripts/sim_trading.sh index 191d8ba..89d3804 100755 --- a/scripts/sim_trading.sh +++ b/scripts/sim_trading.sh @@ -16,7 +16,7 @@ # bash scripts/sim_trading.sh 12 1 # hotter: ~25 fills/s # # Local replica (default): the anonymous controller seeds bot balances. -# Remote/cloud engine: set IC_ENV= (engine | subnet | ic) to target a deployed canister; +# Remote/cloud engine: set IC_ENV= (engine | subnet) to target a deployed canister; # bots self-fund via the public faucet (addTestTokens) — e.g. # IC_ENV=engine bash scripts/sim_trading.sh 6 4 # or IC_ENV=subnet # On mainnet each update call is ~2s, so expect a far lower fill rate and use diff --git a/scripts/simulate_trading.sh b/scripts/simulate_trading.sh index c00d6bd..ef38c11 100755 --- a/scripts/simulate_trading.sh +++ b/scripts/simulate_trading.sh @@ -14,6 +14,14 @@ set -o pipefail export PATH="$HOME/.local/bin:$PATH" +# Scratch-file locations (.run/, not fixed names under sticky /tmp) — see +# scripts/lib/runfiles.sh. This script only READS the price snapshot, but it +# has to look where inject_history.sh wrote it: reading the old /tmp path +# after the writers moved would silently fall back to the hardcoded +# DEFAULT_PRICES and walk a freshly-seeded AMM off its real price. +# shellcheck source=scripts/lib/runfiles.sh +. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/runfiles.sh" + # ── Colors ──────────────────────────────────────────────────────── GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -58,13 +66,13 @@ MAX_QTYS=("0.1" "4.0" "80.0" "3000.0") # Track last known prices (updated when trades occur). Bootstrap from # (a) the canister's getMarkets lastPrice if non-zero, falling back -# to (b) the /tmp/uplands-oracle-prices.txt snapshot if present, +# to (b) the $MDX_ORACLE_PRICES snapshot if present, # falling back to (c) the hardcoded DEFAULT_PRICES. LAST_PRICES=("${DEFAULT_PRICES[@]}") -ORACLE_FILE="/tmp/uplands-oracle-prices.txt" +ORACLE_FILE="$MDX_ORACLE_PRICES" for i in 0 1 2 3; do m="${MARKETS[$i]}"; base="${BASE_TOKENS[$i]}" - lp=$(icp canister call backend getMarkets '()' 2>/dev/null \ + lp=$(icp canister call backend getMarkets '()' --identity anonymous 2>/dev/null \ | awk -v m="$m" '$0 ~ m{f=1} f && /lastPrice/{ v=$3; gsub(/_/,"",v); if (v+0==0) print "0"; else printf "%.8f", v/100000000; exit }') if [ -n "$lp" ] && [ "$lp" != "0.0" ] && [ "$lp" != "0" ]; then LAST_PRICES[$i]="$lp" @@ -112,7 +120,17 @@ err() { echo -e " ${RED}✗${NC} $1"; STAT_ERROR=$((STAT_ERROR + 1)); } section() { echo -e "\n${YELLOW}═══ $1 ═══${NC}"; } dim() { echo -e " ${DIM}$1${NC}"; } -call() { echo "y" | icp canister call backend "$@" 2>&1; } +# Trader actions pass their own --identity. Calls that omit it get +# `--identity anonymous` appended — NEVER the CLI's global default identity, +# which is machine-shared mutable state that other sessions and connectors +# move at will (mdex-process-safety §5). Anonymous signs the market reads +# fine, so identity-less probes stay deterministic. +call() { + case " $* " in + *" --identity "*) echo "y" | icp canister call backend "$@" 2>&1 ;; + *) echo "y" | icp canister call backend "$@" --identity anonymous 2>&1 ;; + esac +} # Integer-money: the backend ledger is Nat base units (10^8). Money args go out # as base-unit integers; prices/qtys parsed back from Candid output (which prints @@ -221,7 +239,7 @@ action_limit_order() { # candles we observed were both this drift mechanism in action.) if echo "$result" | grep -q 'filled\|partiallyFilled'; then local lp_actual - lp_actual=$(icp canister call backend getMarkets '()' 2>/dev/null \ + lp_actual=$(icp canister call backend getMarkets '()' --identity anonymous 2>/dev/null \ | awk -v m="$market" '$0 ~ m{f=1} f && /lastPrice/{ v=$3; gsub(/_/,"",v); if (v+0==0) print "0"; else printf "%.8f", v/100000000; exit }') if [ -n "$lp_actual" ] && [ "$lp_actual" != "0.0" ] && [ "$lp_actual" != "0" ]; then LAST_PRICES[$mi]="$lp_actual" diff --git a/scripts/topup_archive.sh b/scripts/topup_archive.sh index 998cbc0..f6116b5 100755 --- a/scripts/topup_archive.sh +++ b/scripts/topup_archive.sh @@ -18,7 +18,7 @@ # Usage: # bash scripts/topup_archive.sh # local replica, default amount # bash scripts/topup_archive.sh --amount 50t # custom amount -# bash scripts/topup_archive.sh -e ic --identity me # a deployed engine / mainnet +# bash scripts/topup_archive.sh -e engine|subnet --identity me # a deployed mainnet stack # set -euo pipefail export PATH="$HOME/.local/bin:$PATH" @@ -36,6 +36,16 @@ while [ $# -gt 0 ]; do esac done +# If the caller named no identity, pin anonymous rather than inherit the +# CLI's machine-global default, which other sessions and connectors move at +# will (mdex-process-safety §5). Locally anonymous IS the controller; +# against engine/subnet an anonymous top-up fails loudly instead of racing +# the default. +case " ${PASS[*]:-} " in + *" --identity "*) ;; + *) PASS+=(--identity anonymous) ;; +esac + info() { printf '\033[0;36m▶\033[0m %s\n' "$1"; } ok() { printf ' \033[0;32m✓\033[0m %s\n' "$1"; } warn() { printf '\033[1;33m!\033[0m %s\n' "$1"; } diff --git a/scripts/trading_simulation.sh b/scripts/trading_simulation.sh index 36c5342..57ba50c 100755 --- a/scripts/trading_simulation.sh +++ b/scripts/trading_simulation.sh @@ -265,7 +265,7 @@ log "MULTI/DEX trading simulation — $BOTS bots, interval ${INTERVAL}s$($REMOTE $USE_MARGIN || log "margin DISABLED (--no-margin) — spot archetypes only" BOT_PRINCIPALS=() -log "creating + funding identities (spot ~\$100k; margin ~\$$(awk -v c=\"$MARGIN_SEED_CASH\" 'BEGIN{printf \"%.0fk\", (c+60000)/1000}') incl. \$${POOL_FUND_USD:-25000} pool collateral)…" +log "creating + funding identities (spot ~\$100k; margin ~\$$(awk -v c="$MARGIN_SEED_CASH" 'BEGIN{printf "%.0fk", (c+60000)/1000}') incl. \$${POOL_FUND_USD:-25000} pool collateral)…" for i in $(seq 1 "$BOTS"); do id="${BOT_PREFIX}_$i" icp identity new "$id" --storage plaintext >/dev/null 2>&1 || true @@ -341,3 +341,9 @@ while true; do "$(date +%H:%M:%S)" "$((cur - prev))" "$MONITOR_S" "$rate" "${hp:-0}" "${hl:-0}" "${hs:-0}" "$(liq_count)" "$(activity)" prev=$cur done + +# NOTE ON LIVE EDITS: bash reads this file incrementally from a shared inode. +# Editing it IN PLACE while a fleet is executing it corrupts the running +# parse at whatever offset the interpreter has reached (observed 2026-08-01: +# the Phase II starter died at a shifted token mid-funding). Edit via +# write-temp + atomic rename (mv/os.replace), or stop the fleet first. diff --git a/src/backend/ArchiveCanister.mo b/src/backend/ArchiveCanister.mo index 38c85e7..7814635 100644 --- a/src/backend/ArchiveCanister.mo +++ b/src/backend/ArchiveCanister.mo @@ -32,6 +32,7 @@ import Region "mo:core/Region"; import Cycles "mo:core/Cycles"; import CertifiedData "mo:core/CertifiedData"; import Blob "mo:core/Blob"; +import Time "mo:core/Time"; import Prim "mo:⛔"; // rts_memory_size / rts_heap_size for the Stats → Archive panel import Types "lib/Types"; import EventChain "lib/EventChain"; @@ -70,6 +71,33 @@ persistent actor class Archive(owner : Principal) { let dwIndex = List.empty<(Nat64, Nat)>(); var dwCursor : Nat = 0; // globalIndex positions [0, dwCursor) already scanned into dwIndex + // ── Low-memory early warning ────────────────────────────────────── + // Fires when this canister's wasm memory first crosses its + // wasm_memory_threshold setting; re-arms when usage drops back below. The + // archive needs its own hook: the events live in the stable Region, but the + // OFFSET INDEXES (globalIndex, userIndex, dwIndex) are heap-resident and grow + // with the tape forever, so this canister approaches a heap wall the exchange + // canister's hook knows nothing about — and the exchange's hook cannot fire + // for a sidecar in any case. + // + // ⚠️ OPS: a SPAWNED canister inherits the IC default wasm_memory_threshold of + // 0, which means "never warn". Until an operator sets a non-zero threshold on + // each archive (or the exchange sets one at spawn time), this hook cannot + // fire and these fields stay at their zero values — a zero here means "never + // fired OR never armed", and the two are indistinguishable from inside. + // + // Mirrors main.mo's hook: a stable stamp, readable after an upgrade or while + // updates are wedged. main.mo also writes its event log; the archive has no + // log, so `stats()` is the surfacing channel — plus a count, because an + // archive that keeps re-crossing the threshold is a different (and worse) + // story than one that crossed it once. + var lowMemoryAtNs : Int = 0; // Time.now() of the last crossing (0 = never) + var lowMemoryCount : Nat = 0; // how many times it has fired + system func lowmemory() : async* () { + lowMemoryAtNs := Time.now(); + lowMemoryCount += 1; + }; + // True iff `data` can hold `needed` bytes. Region.grow signals failure by // RETURNING 0xFFFF_FFFF_FFFF_FFFF — the build's --max-stable-pages cap, or // the IC refusing this canister more memory — it does not trap. That result @@ -228,35 +256,75 @@ persistent actor class Archive(owner : Principal) { // recomputed hash equals its successor's prevHash. `ok=false` pinpoints // the first broken seq. Anyone may call; the stronger, trustless variant // is the same computation done client-side under the certified head. + // + // BOUNDARY LINKS: `prev` starts empty on every call, so the first event of a + // page has nothing to check its inbound prevHash against. Left that way, a + // paged audit — the usage this docstring recommends — would verify only the + // links INSIDE each page and silently skip the one joining it to the page + // before, ceil(N/P)−1 of them across a run, while still returning ok=true. + // An event whose prevHash was rewritten to point somewhere else would sit + // exactly on such a boundary in the majority of pagings. So when the window + // opens ABOVE the chain anchor, seed `prev` from the preceding stored event + // and re-check that link here. (The client-side verifier in + // src/frontend/src/ledger.js already seeds its slices the same way.) If the + // predecessor cannot be loaded, the link is unverifiable — that is reported + // as ok=false, never passed over: silence is what made this a bug. + // + // `linksChecked` reports how many prevHash links this call actually verified, + // so the COVERAGE of an audit is itself auditable — "ok = true" alone cannot + // distinguish a thorough pass from a vacuous one. A page of k events verifies + // k links when its inbound link was seeded and k−1 when it starts at the + // anchor (whose own prevHash points before this chain), so a paged walk sums + // to exactly the single-call total, N−1. That identity is the regression + // test: it held only by accident of paging before, and now holds by + // construction. tests/test_archive_chain_paged.sh asserts it. public query func verifyChain(fromSeq : Nat, limit : Nat) : async { - checked : Nat; ok : Bool; brokenAt : ?Nat; nextSeq : ?Nat; + checked : Nat; linksChecked : Nat; ok : Bool; brokenAt : ?Nat; nextSeq : ?Nat; } { - let base = switch (firstSeq) { case (?f) { f }; case null { return { checked = 0; ok = true; brokenAt = null; nextSeq = null } } }; - let start = switch (chainStartSeq) { case (?s) { Nat.max(fromSeq, s) }; case null { return { checked = 0; ok = true; brokenAt = null; nextSeq = null } } }; - if (start >= nextExpected) { return { checked = 0; ok = true; brokenAt = null; nextSeq = null } }; + let empty = { checked = 0; linksChecked = 0; ok = true; brokenAt = null; nextSeq = null }; + let base = switch (firstSeq) { case (?f) { f }; case null { return empty } }; + let anchor = switch (chainStartSeq) { case (?s) { s }; case null { return empty } }; + let start = Nat.max(fromSeq, anchor); + if (start >= nextExpected) { return empty }; let capped = Nat.min(limit, 10_000); var s = start; var prev : ?Types.UserEvent = null; + // Seed the inbound link unless the window starts AT the anchor, whose own + // prevHash points before this chain and is unverifiable by definition. + // start > anchor ≥ base, so start-1-base is in range and never underflows. + if (start > anchor) { + switch (loadAt(globalIndex, start - 1 - base)) { + case (?p) { prev := ?p }; + case null { + // The predecessor is unreadable, so this page's inbound link cannot + // be verified. Report it — reporting ok=true here is precisely the + // silent hole being fixed. + return { checked = 0; linksChecked = 0; ok = false; brokenAt = ?(start - 1); nextSeq = null }; + }; + }; + }; var checked = 0; + var links = 0; while (s < nextExpected and checked < capped) { switch (loadAt(globalIndex, s - base)) { case (?e) { switch (prev) { case (?p) { if (e.prevHash != ?EventChain.hash(p)) { - return { checked; ok = false; brokenAt = ?e.seq; nextSeq = null }; + return { checked; linksChecked = links; ok = false; brokenAt = ?e.seq; nextSeq = null }; }; + links += 1; }; case null {}; }; prev := ?e; }; - case null { return { checked; ok = false; brokenAt = ?s; nextSeq = null } }; + case null { return { checked; linksChecked = links; ok = false; brokenAt = ?s; nextSeq = null } }; }; checked += 1; s += 1; }; - { checked; ok = true; brokenAt = null; nextSeq = if (s < nextExpected) { ?s } else { null } }; + { checked; linksChecked = links; ok = true; brokenAt = null; nextSeq = if (s < nextExpected) { ?s } else { null } }; }; // The caller's own events, newest-first. `offset` counts back from the @@ -354,6 +422,11 @@ persistent actor class Archive(owner : Principal) { heapLiveBytes : Nat; // live heap (the offset indexes; the events live in the Region) chainStartSeq : ?Nat; // links verify from here forward (null = no chain yet) chainHead : ?Blob; // running chain hash (also in certified_data) + // When the lowmemory() threshold hook last fired (0 = never fired, OR the + // canister's wasm_memory_threshold is still the spawn default of 0 and the + // hook is unarmed — see the hook), and how many times. + lowMemoryAtNs : Int; + lowMemoryCount : Nat; } { { firstSeq; @@ -366,6 +439,8 @@ persistent actor class Archive(owner : Principal) { heapLiveBytes = Prim.rts_heap_size(); chainStartSeq; chainHead; + lowMemoryAtNs; + lowMemoryCount; }; }; diff --git a/src/backend/lib/Liquidator.mo b/src/backend/lib/Liquidator.mo index 09f0006..7cb44ac 100644 --- a/src/backend/lib/Liquidator.mo +++ b/src/backend/lib/Liquidator.mo @@ -28,6 +28,7 @@ import Types "Types"; import Fixed "Fixed"; import Int "mo:core/Int"; import Nat "mo:core/Nat"; +import List "mo:core/List"; import Accounts "Accounts"; import MarginEngine "MarginEngine"; import BorrowEngine "BorrowEngine"; @@ -37,6 +38,50 @@ module { public type PriceLookup = Types.TokenId -> ?Nat; + // How many (debt, collateral) seizes one tryLiquidate pass may perform. This + // is a MESSAGE-SIZE bound, not a solvency judgement: each iteration re-runs a + // full getHealth and walks the collateral set, so the budget is what keeps a + // pass inside the instruction limit. With 5 collateral tokens against 5 + // borrowable ones a genuinely mixed portfolio can want more than this, so + // hitting the bound is an ordinary outcome and must be classified as an + // unfinished close (see the classifier at the foot of tryLiquidate), never as + // insolvency. Raising it would make exhaustion rarer without making the + // distinction less necessary. + public let MAX_SEIZE_ITERS : Nat = 8; + + // How a pass that DID seize something is reported, given whether the user is + // still liquidatable and whether the seize loop ran out of its iteration + // budget. Split out as a pure function because this one line is the whole + // difference between a partial close and a write-off, and driving the loop + // all the way to its bound from a unit test needs a portfolio shape that is + // fiddly to construct and easy to invalidate — the decision deserves to be + // assertable directly. + // + // #insolvent is a claim that COLLATERAL is exhausted, and health alone does + // not establish that. The loop has two very different terminal states that + // both leave a user liquidatable: it walked every collateral and none could + // move the debt (genuinely insolvent), or it simply ran out of iterations + // mid-walk with seizable collateral still standing. Reporting the second as + // #insolvent hands the caller a user whose debt it then writes off WHOLESALE + // — main.mo answers #insolvent with absorbBadDebt, which clears every + // remaining loan and socialises the residual to the insurance pool and then + // to AMM LPs — while the user keeps the collateral the pass never reached. + // That books a fully-collateralised position as a loss. + // + // A budget-exhausted pass is a PARTIAL close, which is what #liquidated + // already means here ("possibly partial-to-target"): real value moved, the + // penalty was really earned, and the user stays in `loans`, so the next sweep + // resumes from the improved health. Progress is monotonic per pass, so a user + // needing more than one budget converges across ticks rather than being + // written off inside one. + public func classifySeizingPass( + stillLiquidatable : Bool, + budgetExhausted : Bool, + event : Types.LiquidationEvent, + ) : Types.LiquidationOutcome { + if (stillLiquidatable and not budgetExhausted) { #insolvent(event) } else { #liquidated(event) }; + }; + func priceOf(token : Types.TokenId, priceLookup : PriceLookup) : Nat { if (token == Types.QUOTE_TOKEN) { return Fixed.SCALE }; switch (priceLookup(token)) { case (?p) { p }; case null { 0 } }; @@ -64,19 +109,81 @@ module { ?best; }; + // ── The repay a seize would actually produce ─────────────────── + // Principal (in DEBT-token units) that seizing `qty` units of `coll` would + // write off. Mirrors seizeOnce's derivation EXACTLY — same order, same + // roundings — and is called by BOTH, so the selection filter and the + // execution can never drift apart. + // + // The floor to zero is the whole of the §1.3 defect. A cross-token seize + // converts through USD, `Fixed.div` rounds DOWN, and a collateral balance + // worth less than ONE base unit of the debt token therefore converts to + // grossDebtUnits = 0 → toRepay = 0 → BorrowEngine.writeOffLoan rejects a + // zero amount. This is NOT a "one base unit of ICPUSD" window: ANY dust + // under the debt token's unit price lands in it. And it is TWICE as wide as + // that, because /pm floors a second time — against BTC at $84,000, q ICPUSD + // units give ⌊q/84_000⌋ gross and then ⌊gross/1.05⌋, which is zero for every + // q < 168_000, i.e. anything below $0.00168. The same-token path floors the + // same way, at qty ≤ 1. + func derivedRepay( + qty : Nat, + coll : Types.CollateralValuation, + debt : Types.DebtEntry, + priceLookup : PriceLookup, + ) : Nat { + if (qty == 0 or debt.principal == 0) { return 0 }; + let collPrice = coll.refPrice; + if (collPrice == 0) { return 0 }; + let pm = Fixed.SCALE + Types.LIQUIDATION_PENALTY; // 1.05 at 10^8 + if (debt.token == coll.token) { + // Direct path: the seized units ARE the gross proceeds. + return Nat.min(debt.principal, Fixed.div(qty, pm, false)); + }; + let dPrice = priceOf(debt.token, priceLookup); + if (dPrice == 0) { return 0 }; + // Seized collateral value, expressed in debt-token units. + let grossDebtUnits = Fixed.div(Fixed.mul(qty, collPrice, false), dPrice, false); + Nat.min(debt.principal, Fixed.div(grossDebtUnits, pm, false)); + }; + + func isTried(tried : List.List, token : Types.TokenId) : Bool { + for (t in List.values(tried)) { if (t == token) { return true } }; + false; + }; + // Pick the collateral token to seize against this debt. Preference: - // 1. Same token as the debt (direct repay, no conversion). - // 2. ICPUSD when the debt is a base asset (and vice-versa). - // 3. any other base asset — e.g. a short owing X but holding only another - // base asset, which must be liquidatable rather than accrue bad debt. - // Cross-token seizes (2 + 3) are absorbed into the vault at the oracle mid - // by seizeOnce, so no token actually trades; this just chooses the richest - // seizable token. Returns null only if the user holds NO seizable collateral. + // 1. Same token as the debt (direct repay, no conversion, no dependence + // on an oracle price for the debt token). + // 2. Otherwise the highest-$ seizable token — ICPUSD or a base asset, + // ranked purely on contribUsd. This is what lets a short owing X but + // holding another base asset be liquidated rather than accrue bad debt, + // and it maximises the chance of covering the debt in one pass. + // Cross-token seizes are absorbed into the vault at the oracle mid by + // seizeOnce, so no token actually trades; this just chooses the richest + // seizable token. + // + // TWO hard rules, both of them the §1.3 fix: + // • A candidate must clear a VALUE FLOOR, not merely `balance > 0`: + // seizing its WHOLE balance must repay at least one base unit of + // principal. `balance > 0` is what let a dust payment mask a pool's + // real collateral — the seize unwound, the driver stopped, and the + // position stayed un-liquidatable on every 30s sweep forever. The + // floor is evaluated at `balance` (the maximum `partialSeizeQty` can + // ever return for this token) so it can never reject a candidate that + // would have worked; seizeOnce's #skip catches the residual case where + // the SIZED seize floors to zero. + // • ICPUSD is NOT preferred unconditionally. It used to win over a + // `bestBase` of any size, so 1 base unit of ICPUSD outranked a whole + // pool of BTC. + // `tried` excludes collateral the driver has already attempted for this + // debt, so the caller can walk every candidate. Returns null once no + // untried, above-floor collateral is left. func pickCollateral( margin : MarginEngine.MarginState, accounts : Accounts.AccountState, user : Principal, - debtToken : Types.TokenId, + debt : Types.DebtEntry, + tried : List.List, priceLookup : PriceLookup, ) : ?Types.CollateralValuation { // Seize sizing must use SEIZABLE (un-reserved) balance — reserved @@ -85,55 +192,30 @@ module { // check, which counts reserved). let noReserved : MarginEngine.ReservedLookup = func(_, _) { 0 }; let vals = MarginEngine.valuations(margin, accounts, noReserved, user, priceLookup); - // First pass: exact-token match (direct repay path). - var sameTokenHit : ?Types.CollateralValuation = null; + var sameToken : ?Types.CollateralValuation = null; + var bestCross : ?Types.CollateralValuation = null; for (v in vals.vals()) { - if (v.token == debtToken and v.balance > 0) { - sameTokenHit := ?v; - }; - }; - switch (sameTokenHit) { - case (?v) { return ?v }; - case null { }; - }; - // Second pass: one-leg path (X-ICPUSD market exists for every - // tradable base token). - // debt is ICPUSD → any non-ICPUSD collateral works. - // debt is X → ICPUSD collateral works. - if (debtToken == Types.QUOTE_TOKEN) { - // Prefer the highest-$ collateral so we maximise the chance of - // covering the full debt in one pass. - var best : ?Types.CollateralValuation = null; - for (v in vals.vals()) { - if (v.token != Types.QUOTE_TOKEN and v.balance > 0) { - switch (best) { - case null { best := ?v }; - case (?b) { if (v.contribUsd > b.contribUsd) { best := ?v } }; - }; - }; - }; - best - } else { - // Debt is a non-ICPUSD asset. - // Prefer ICPUSD collateral → single-leg buy (debt_token with ICPUSD). - // Fall back to the highest-$ OTHER base asset → two-leg route - // (sell it for ICPUSD, then buy debt_token). This is what lets a - // short (owes BTC, holds only SOL) be liquidated, rather than - // sitting un-recoverable. - var icpusd : ?Types.CollateralValuation = null; - var bestBase : ?Types.CollateralValuation = null; - for (v in vals.vals()) { - if (v.token == Types.QUOTE_TOKEN and v.balance > 0) { - icpusd := ?v; - } else if (v.token != debtToken and v.balance > 0) { - switch (bestBase) { - case null { bestBase := ?v }; - case (?b) { if (v.contribUsd > b.contribUsd) { bestBase := ?v } }; + if (v.balance > 0 + and not isTried(tried, v.token) + and derivedRepay(v.balance, v, debt, priceLookup) > 0) { + if (v.token == debt.token) { + sameToken := ?v; + } else { + switch (bestCross) { + case null { bestCross := ?v }; + case (?b) { + // Tie-break by token name so the choice is deterministic and + // independent of MARGIN_COLLATERAL_TOKENS' order (as largestDebt). + if (v.contribUsd > b.contribUsd + or (v.contribUsd == b.contribUsd and v.token < b.token)) { + bestCross := ?v; + }; + }; }; }; }; - switch (icpusd) { case (?v) { ?v }; case null { bestBase } }; }; + switch (sameToken) { case (?v) { ?v }; case null { bestCross } }; }; // ── Partial-close seize sizing (Phase 3A) ────────────────────── @@ -226,7 +308,9 @@ module { let debt = switch (largestDebt(loans, user, priceLookup)) { case (?d) { d }; case null { return null }; }; - let coll = switch (pickCollateral(margin, accounts, user, debt.token, priceLookup)) { + // No exclusions here — the netting planner gets one shot per user, and + // pickCollateral already refuses candidates that can't repay anything. + let coll = switch (pickCollateral(margin, accounts, user, debt, List.empty(), priceLookup)) { case (?c) { c }; case null { return null }; }; let crossToken = debt.token != coll.token; @@ -364,8 +448,20 @@ module { // Seize + repay for ONE (debt, collateral) pair, sized toward TARGET // from the CURRENT health `h`. Mutates balances + loans in place; - // rolls back its own seize if the repay/valuation can't complete. - // Returns what was repaid/seized, or #err. + // rolls back its own seize if the repay/valuation can't complete — + // EVERY failing exit past the seize goes through unwindSeize(), the + // direct path included (it used to return #err with the seize still + // applied, quietly confiscating the collateral on every sweep). + // + // Three outcomes, and the distinction matters: + // #ok — debt was repaid. + // #skip — this collateral CANNOT repay anything (its value floors to + // zero against the debt token). Nothing was touched. The driver + // must move to the next candidate: treating this as #err is + // what let one dust balance hide a pool's real collateral + // forever (docs/issue-triage-2026-08.md §1.3). + // #err — something transient/unexpected (missing oracle price, failed + // subtraction). State is unchanged; the caller may retry. func seizeOnce( h : Types.MarginHealth, debt : Types.DebtEntry, @@ -376,13 +472,39 @@ module { collToken : Types.TokenId; collSeized : Nat; collSeizedUsd : Nat; proceedsUsd : Nat; }; + #skip : Text; #err : Text; } { let crossToken = debt.token != coll.token; let collPrice = coll.refPrice; if (collPrice == 0) { return #err("no oracle price for collateral " # coll.token) }; + let dPrice = priceOf(debt.token, priceLookup); + // Checked BEFORE the seize (it used to seize, then unwind) — nothing to + // roll back, and the cross-token branch below can rely on dPrice > 0. + if (crossToken and dPrice == 0) { + return #err("no oracle price for debt token " # debt.token); + }; let seizeQty = partialSeizeQty(h, debt, coll); - if (seizeQty == 0) { return #err("seize qty rounds to zero") }; + if (seizeQty == 0) { return #skip("seize qty rounds to zero for " # coll.token) }; + + // Guaranteed-penalty split: of the `gross` proceeds (debt-token units) + // available to apply, repay R = gross/(1+p) (derivedRepay) and KEEP + // penalty = p·R in the vault as insurance surplus; refund anything + // beyond R + penalty to the user. So every liquidation charges exactly + // the 5% penalty (self-funding the buffer), never more (over-recovery + // refunded). + // + // Derive the repay BEFORE touching any balance. If the seize is worth + // less than one base unit of the debt token this floors to zero, + // writeOffLoan would reject it, and the whole seize would have to + // unwind — so bail out cleanly instead, WITHOUT moving anything, and + // let the driver try the next collateral. + let toRepay = derivedRepay(seizeQty, coll, debt, priceLookup); + if (toRepay == 0) { + return #skip( + "seizing " # coll.token # " repays nothing — worth less than one base unit of " # debt.token + ); + }; // Transfer the seized collateral to the vault (the balance // subtraction IS the collateral reduction in cross-margin). @@ -391,20 +513,22 @@ module { }; Accounts.addBalance(accounts, vaultPrincipal, coll.token, seizeQty); - // Guaranteed-penalty split: of the `gross` proceeds (debt-token units) - // available to apply, repay R = gross/(1+p) and KEEP penalty = p·R in - // the vault as insurance surplus; refund anything beyond R + penalty - // to the user. So every liquidation charges exactly the 5% penalty - // (self-funding the buffer), never more (over-recovery refunded). - let pm = Fixed.SCALE + Types.LIQUIDATION_PENALTY; // 1.05 at 10^8 - let dPrice = priceOf(debt.token, priceLookup); + // The ONE unwind, shared by both routes: restore the pre-seize balances + // exactly. Any failure below must call this before returning #err. + func unwindSeize() { + ignore Accounts.subtractBalance(accounts, vaultPrincipal, coll.token, seizeQty); + Accounts.addBalance(accounts, user, coll.token, seizeQty); + }; + + // Same repay call for both routes — on the direct path coll.token IS + // debt.token. On failure the seize unwinds; the user keeps their funds. + switch (BorrowEngine.repayFromVault(loans, vaultPrincipal, accounts, user, debt.token, toRepay, now)) { + case (#ok(_)) { }; + case (#err(e)) { unwindSeize(); return #err("repay failed: " # e) }; + }; + let (debtRepaidPrincipal, proceedsUsd) = if (not crossToken) { // Direct path: seized token IS the debt token; gross = seizeQty. - let toRepay = Nat.min(debt.principal, Fixed.div(seizeQty, pm, false)); - switch (BorrowEngine.repayFromVault(loans, vaultPrincipal, accounts, user, coll.token, toRepay, now)) { - case (#ok(_)) { }; - case (#err(e)) { return #err("repay failed: " # e) }; - }; let penaltyKept = Fixed.mul(toRepay, Types.LIQUIDATION_PENALTY, false); let refundable = SafeMath.subOrZero(seizeQty, toRepay + penaltyKept); if (refundable > 0) { @@ -434,23 +558,7 @@ module { // this: taker and maker are the same principal, so every leg nets to // zero and nothing converts. Absorbing into inventory is the only // conserved path — the vault KEEPS the collateral; nothing is minted. - if (dPrice == 0) { - // Can't value the debt token — undo the seize so a later pass retries. - ignore Accounts.subtractBalance(accounts, vaultPrincipal, coll.token, seizeQty); - Accounts.addBalance(accounts, user, coll.token, seizeQty); - return #err("no oracle price for debt token " # debt.token); - }; - // Seized collateral value, expressed in debt-token units. - let grossDebtUnits = Fixed.div(Fixed.mul(seizeQty, collPrice, false), dPrice, false); - let toRepay = Nat.min(debt.principal, Fixed.div(grossDebtUnits, pm, false)); - switch (BorrowEngine.repayFromVault(loans, vaultPrincipal, accounts, user, debt.token, toRepay, now)) { - case (#ok(_)) { }; - case (#err(e)) { - ignore Accounts.subtractBalance(accounts, vaultPrincipal, coll.token, seizeQty); - Accounts.addBalance(accounts, user, coll.token, seizeQty); - return #err("repay failed: " # e); - }; - }; + // (dPrice > 0 and the repay both established above, before the seize.) // The vault retains only enough collateral to cover the repaid debt + // the 5% penalty (both debt-token units → collateral units); the rest is // refunded. Normal partial close: toRepay = grossDebtUnits/pm, so nothing @@ -493,8 +601,13 @@ module { var primCollUsd : Int = -1; var madeProgress = false; var stopErr : ?Text = null; + // Did the loop stop because it ran out of ITERATIONS, as opposed to + // reaching target / clearing the debt / running out of collateral? The + // distinction is load-bearing at the classifier below: only the latter + // reasons say anything about the user's solvency. + var budgetExhausted = false; var iter = 0; - label L while (iter < 8) { + label L while (iter < MAX_SEIZE_ITERS) { iter += 1; let h = BorrowEngine.getHealth(loans, margin, accounts, reserved, user, priceLookup); if (h.debtUsd == 0) { break L }; @@ -502,29 +615,69 @@ module { let debt = switch (largestDebt(loans, user, priceLookup)) { case (?d) { d }; case null { break L }; }; - let coll = switch (pickCollateral(margin, accounts, user, debt.token, priceLookup)) { - case (?c) { c }; - case null { break L }; // no seizable collateral → handled below - }; - switch (seizeOnce(h, debt, coll)) { - case (#ok(r)) { - madeProgress := true; - totDebtRepaidUsd += r.debtRepaidUsd; - totProceedsUsd += r.proceedsUsd; - if (r.debtRepaidUsd > primDebtUsd) { - primDebtUsd := r.debtRepaidUsd; primDebtToken := r.debtToken; primDebtRepaid := r.debtRepaid; + // Walk EVERY seizable collateral for this debt until one moves. The old + // code took pickCollateral's single answer and did `break L` the moment + // it failed — so one dust balance (which pickCollateral preferred + // unconditionally) meant the pool's REAL collateral was never examined, + // on every sweep, forever (docs/issue-triage-2026-08.md §1.3). A failed + // candidate is marked tried and the next-best is picked instead; we only + // give up on this debt once every candidate has been tried. + // + // `tried` is scoped to THIS debt: a token that can't repay debt A may + // well repay debt B, and a successful seize changes health, so the next + // outer pass starts from a clean slate. Bounded by the collateral-token + // set (pickCollateral never returns a tried token, so this terminates + // on its own; the bound is belt-and-braces on a money path). + let tried = List.empty(); + var advanced = false; + var cand = 0; + label C while (cand < Types.MARGIN_COLLATERAL_TOKENS.size()) { + cand += 1; + let coll = switch (pickCollateral(margin, accounts, user, debt, tried, priceLookup)) { + case (?c) { c }; + case null { break C }; // no untried seizable collateral → handled below + }; + switch (seizeOnce(h, debt, coll)) { + case (#ok(r)) { + madeProgress := true; + advanced := true; + totDebtRepaidUsd += r.debtRepaidUsd; + totProceedsUsd += r.proceedsUsd; + if (r.debtRepaidUsd > primDebtUsd) { + primDebtUsd := r.debtRepaidUsd; primDebtToken := r.debtToken; primDebtRepaid := r.debtRepaid; + }; + if (r.collSeizedUsd > primCollUsd) { + primCollUsd := r.collSeizedUsd; primCollToken := r.collToken; primCollSeized := r.collSeized; + }; + break C; // health moved — re-snapshot and re-pick from the top. }; - if (r.collSeizedUsd > primCollUsd) { - primCollUsd := r.collSeizedUsd; primCollToken := r.collToken; primCollSeized := r.collSeized; + case (#skip(_)) { + // Not a failure: this collateral simply cannot repay anything + // (dust). Nothing was touched. Try the next-best token. + List.add(tried, coll.token); + }; + case (#err(e)) { + // seizeOnce rolled back its own work. Keep the FIRST reason in + // case nothing at all turns out to be seizable, then move on — + // a bad oracle price for ONE token must not veto the others. + switch (stopErr) { + case null { if (not madeProgress) { stopErr := ?e } }; + case (?_) { }; + }; + List.add(tried, coll.token); }; - }; - case (#err(e)) { - // seizeOnce rolled back its own work. Stop here; if we'd - // already made progress, finalise with what we got. - if (not madeProgress) { stopErr := ?e }; - break L; }; }; + // Every candidate tried and none could move this debt → stop. (Falls + // through to #err when a transient reason was recorded, otherwise to + // #insolvent, so the insurance fund closes the position instead of + // leaving it a perpetually-liquidatable zombie.) + if (not advanced) { break L }; + // We advanced and the bound is about to stop us: the collateral walk is + // unfinished, not exhausted. Recorded HERE rather than inferred from + // `iter` afterwards, because a benign break on the final iteration + // leaves the same `iter` value and means the opposite thing. + if (iter >= MAX_SEIZE_ITERS) { budgetExhausted := true }; }; // Nothing seized at all. @@ -570,13 +723,6 @@ module { healthAfter = healthAfter.healthRatio; timestamp = now; }; - // Still liquidatable after exhausting collateral → insolvent (Phase 4 - // insurance-fund socialisation). Otherwise a successful (possibly - // partial-to-target) close. - if (healthAfter.isLiquidatable) { - #insolvent(event) - } else { - #liquidated(event) - }; + classifySeizingPass(healthAfter.isLiquidatable, budgetExhausted, event); }; }; diff --git a/src/backend/lib/LiquidityManager.mo b/src/backend/lib/LiquidityManager.mo index d5e6d8e..c9d8a50 100644 --- a/src/backend/lib/LiquidityManager.mo +++ b/src/backend/lib/LiquidityManager.mo @@ -34,6 +34,19 @@ module { price : Nat, quantity : Nat, ) : { #ok; #err : Text } { + // Per-level congestion cap (see Types.MAX_ORDERS_PER_PRICE_LEVEL): a + // price already holding the max resting orders takes no more. Checked + // FIRST — a full level rejects regardless of balances — and side-scoped: + // only the caller's own (side, price) level counts, so a stacked opposite + // side never blocks an order that would cross it. Internal placements + // (AMM ladder, deferred releases resting a remainder) do not route + // through this validation and are exempt by construction. + if (OrderBook.getLevelOrderCount(store, marketId, side, price) >= Types.MAX_ORDERS_PER_PRICE_LEVEL) { + return #err( + "Price level full: " # Nat.toText(Types.MAX_ORDERS_PER_PRICE_LEVEL) # + " orders already rest at this exact price on this side. Choose a different price." + ); + }; switch (side) { case (#buy) { let orderValue = Fixed.mul(quantity, price, true); diff --git a/src/backend/lib/MatchingEngine.mo b/src/backend/lib/MatchingEngine.mo index fe3a3cf..c19b0cd 100644 --- a/src/backend/lib/MatchingEngine.mo +++ b/src/backend/lib/MatchingEngine.mo @@ -120,6 +120,30 @@ module { onTradeFees = func(_, _, _) {}; // legacy/test: nothing to attribute }; + // ── Per-call work bounds ─────────────────────────────────────── + // One matcher invocation consumes at most this many match-loop iterations, + // counting EVERY iteration — fills and skips alike, since a skipped maker + // (non-takeable AMM quote, expired, self-cross, pending-locked, unaffordable) + // costs a book re-walk too. This is the hard per-message bound on matcher + // work: with the level index keyed by (timestamp, id) each iteration is + // O(log book + excluded-set), so a capped call is bounded by construction + // no matter how many orders rest at one price. Uncapped, a single taker + // sweeping a stacked level was the same O(K²) shape that trapped + // processDeferredExpiry at ~3k same-price orders (40B instruction limit). + // On cap-out: a #market taker returns its remainder to the caller (IOC — + // re-defer or drop, the existing semantics); a #limit taker rests it. + public let MAX_MATCH_ITERATIONS_PER_CALL : Nat = 256; + // The noPartialFill (FOK) pre-check promises only what fits in HALF the + // iteration budget of distinct makers. The live loop spends at most 2 + // iterations per maker the simulation examined (a partially pending-locked + // maker is touched twice: fill, then exhaust on the re-find), so a promise + // built from ≤128 makers always completes within the 256-iteration cap — + // an FOK must never pass its pre-check and then stop short on iterations. + // Practical meaning: an all-or-nothing order can sweep at most 128 distinct + // resting makers; larger jobs are killed at the pre-check, not partially + // filled. + public let FOK_SIM_MAKER_BUDGET : Nat = 128; + public type MatchResult = { trades : [Types.Trade]; // Pending matches created during this call (only non-empty when one @@ -215,7 +239,13 @@ module { // settled — and break with `availableForFill < eps`. let exhausted = Map.empty(); - label matchLoop while (remainingQty > 0) { + // Per-call work bound — see MAX_MATCH_ITERATIONS_PER_CALL. Counted at the + // top so every path through the body (fill, skip-continue, cancel-continue) + // spends exactly one unit. + var iterations : Nat = 0; + + label matchLoop while (remainingQty > 0 and iterations < MAX_MATCH_ITERATIONS_PER_CALL) { + iterations += 1; let best = switch (OrderBook.findBestMatchExcluding(store, marketId, side, ?exhausted)) { case null { break matchLoop }; case (?b) { b }; @@ -456,13 +486,18 @@ module { // non-takeable (AMM) / expired / pending-locked makers, count up to the // taker's balance, and stop at the first maker it can't afford (orders are // walked best-price-first, so a worse-priced maker is never cheaper). - // Within a level all orders share the price, so the order we consume them in - // cannot change the `available >= quantity` decision the sole caller (the - // noPartialFill pre-check) makes — hence no intra-level sort is needed. + // Intra-level order is the (timestamp, id) key order — identical to the + // live loop's touch order, which the maker budget below depends on: the + // promise must be built from exactly the makers the live loop will reach + // first. let levelIter = switch (makerSide) { case (#sell) { Map.entries(lvls) }; // lowest ask first case (#buy) { Map.reverseEntries(lvls) }; // highest bid first }; + // FOK maker budget — see FOK_SIM_MAKER_BUDGET. Counts every OPEN maker + // the walk reaches (skipped ones included: the live loop spends an + // iteration excluding those too). + var examined : Nat = 0; label simLoop for ((price, lvl) in levelIter) { // Levels are monotonic in price, so once one falls outside the slippage // band every worse level does too — stop scanning. @@ -472,16 +507,22 @@ module { }; if (not withinSlip) { break simLoop }; - for ((id, _) in Map.entries(lvl)) { + label lvlWalk for (((_ts, id), _) in Map.entries(lvl)) { switch (Map.get(store.orders, Nat.compare, id)) { case null {}; case (?o) { + if (not OrderBook.isOpen(o)) { continue lvlWalk }; + // Budget check BEFORE any skip predicate: every open maker the + // walk reaches here is one the live loop would spend an iteration + // on (fill, or skip-and-exclude), so all of them draw the budget. + examined += 1; + if (examined > FOK_SIM_MAKER_BUDGET) { break simLoop }; // Skip own resting orders (the engine's self-trade prevention does // too) so a noPartialFill buy isn't told it can fill against itself. // Same BENEFICIAL-owner comparison as the live loop — if the sim // counted a pool's depth that the live loop then refuses, FOK would // pass a pre-check and partially fill. - if (OrderBook.isOpen(o) and not ctx.isNonTakeable(o.id, o.owner) and not ctx.isExpired(o.id) + if (not ctx.isNonTakeable(o.id, o.owner) and not ctx.isExpired(o.id) and not Principal.equal(ctx.beneficialOwner(o.owner), ctx.beneficialOwner(taker))) { let pendingLocked = ctx.getMakerPending(o.id); let obRem = OrderBook.remaining(o); @@ -599,7 +640,13 @@ module { // maker instead of returning a stub-fill. See market-side comment. let exhausted = Map.empty(); - label matchLoop while (remainingQty > 0) { + // Per-call work bound — see MAX_MATCH_ITERATIONS_PER_CALL. A capped-out + // crossing limit rests its remainder exactly like any partial fill; the + // rested order is immediately the side's best and later flow finishes it. + var iterations : Nat = 0; + + label matchLoop while (remainingQty > 0 and iterations < MAX_MATCH_ITERATIONS_PER_CALL) { + iterations += 1; let best = switch (OrderBook.findBestMatchExcluding(store, marketId, side, ?exhausted)) { case null { break matchLoop }; case (?b) { b }; diff --git a/src/backend/lib/OrderBook.mo b/src/backend/lib/OrderBook.mo index 45b1b31..cbc03fb 100644 --- a/src/backend/lib/OrderBook.mo +++ b/src/backend/lib/OrderBook.mo @@ -5,6 +5,7 @@ import Fixed "Fixed"; import Int "mo:core/Int"; import Iter "mo:core/Iter"; import Option "mo:core/Option"; +import Order "mo:core/Order"; import Principal "mo:core/Principal"; import Text "mo:core/Text"; import Types "Types"; @@ -20,12 +21,22 @@ module { // ── Secondary indexes ────────────────────────────────────────── openOrdersByMarketSide : Map.Map>; // "MARKET:side" → {orderId} // Price-ordered level index per market-side: "MARKET:side" → (price → - // {orderId}). mo:core Map is a sorted tree, so the touch is the min - // (asks) / max (bids) key — giving O(log N) best-price lookup instead - // of an O(N) linear scan over every resting order (the bottleneck that - // let a growing book peg the canister). Maintained alongside + // {(timestamp, orderId)}). mo:core Map is a sorted tree, so the touch is + // the min (asks) / max (bids) key — giving O(log N) best-price lookup + // instead of an O(N) linear scan over every resting order (the bottleneck + // that let a growing book peg the canister). Maintained alongside // openOrdersByMarketSide in add/removeFromOpenIndexes. - levelsByMarketSide : Map.Map>>; + // + // The INNER set is keyed by (timestamp, id) — the book's time-priority + // order — so the level's FIRST entry IS its priority head and + // findBestMatchExcluding reads it in O(log K) instead of walking all K + // same-price orders per call. That walk made a taker sweeping a level + // O(K²), which is what let ~3k orders stacked at ONE price trap the + // 40B-instruction limit (the measured processDeferredExpiry wedge). + // Keying by id alone can't replace it: a staged order rests under a + // FRESH id at release but keeps its SUBMISSION timestamp, so id order + // and time-priority order genuinely diverge within a level. + levelsByMarketSide : Map.Map>>; tradesByMarket : Map.Map>; openOrdersByUser : Map.Map>; // principal text → {orderId} // Aggregated depth per market-side: "MARKET:side" → (price → (remaining @@ -86,7 +97,7 @@ module { var nextTradeId = 1; trades = List.empty(); openOrdersByMarketSide = Map.empty>(); - levelsByMarketSide = Map.empty>>(); + levelsByMarketSide = Map.empty>>(); tradesByMarket = Map.empty>(); openOrdersByUser = Map.empty>(); levelAggByMarketSide = Map.empty>(); @@ -111,6 +122,18 @@ module { Principal.toText(user) # "|" # marketId; }; + // Time-priority order for a level's inner set: earliest submission + // timestamp first, id as the tie-break (ids are unique, so keys are too). + // This is EXACTLY the priority findBestMatchExcluding used to compute by + // walking the whole level — encoded in the key so the head is O(log K). + // Public: the upgrade migration rebuilds level maps with it. + public func levelKeyCompare(a : (Int, Nat), b : (Int, Nat)) : Order.Order { + switch (Int.compare(a.0, b.0)) { + case (#equal) { Nat.compare(a.1, b.1) }; + case (other) { other }; + }; + }; + // ── Incremental aggregate maintenance ────────────────────────── // All order state transitions flow through createOrder / cancelOrder / // fillOrder / adjustOrderQuantity, and open-set membership through @@ -174,16 +197,17 @@ module { Map.add(msSet, Nat.compare, order.id, true); Map.add(store.openOrdersByMarketSide, Text.compare, msKey, msSet); - // Price-level index (ordered by price → O(log N) best-price lookup). + // Price-level index (ordered by price → O(log N) best-price lookup; + // inner set ordered by (timestamp, id) → O(log K) time-priority head). let lvls = switch (Map.get(store.levelsByMarketSide, Text.compare, msKey)) { - case null { Map.empty>() }; + case null { Map.empty>() }; case (?m) { m }; }; let lvl = switch (Map.get(lvls, Nat.compare, order.price)) { - case null { Map.empty() }; + case null { Map.empty<(Int, Nat), Bool>() }; case (?s) { s }; }; - Map.add(lvl, Nat.compare, order.id, true); + Map.add(lvl, levelKeyCompare, (order.timestamp, order.id), true); Map.add(lvls, Nat.compare, order.price, lvl); Map.add(store.levelsByMarketSide, Text.compare, msKey, lvls); @@ -213,15 +237,18 @@ module { }; }; - // Price-level index — remove the id and prune the level if now empty - // (so minEntry/maxEntry never land on an empty price). + // Price-level index — remove the entry and prune the level if now empty + // (so minEntry/maxEntry never land on an empty price). The key is + // reconstructible because timestamp and id are immutable for the life of + // an order (fills/adjusts touch quantity and status only), and callers + // pass the pre-mutation record. switch (Map.get(store.levelsByMarketSide, Text.compare, msKey)) { case null {}; case (?lvls) { switch (Map.get(lvls, Nat.compare, order.price)) { case null {}; case (?lvl) { - ignore Map.delete(lvl, Nat.compare, order.id); + ignore Map.delete(lvl, levelKeyCompare, (order.timestamp, order.id)); if (Map.size(lvl) == 0) { ignore Map.delete(lvls, Nat.compare, order.price); } else { @@ -233,13 +260,25 @@ module { }; }; - // User index + // User index — remove the id and prune the OUTER entry once the user's + // last open order leaves, exactly as the price-level index does above. + // Without the prune the map keeps an (empty) entry for every principal + // that has EVER placed an order, for the life of the canister: unbounded + // growth in the upgrade-carried heap, and an ever-longer full-map walk + // for main.mo's sweepStaleUserOrders and tickTier, which both iterate it + // whole. Nothing reads an empty entry — every accessor here reports the + // same 0/[] for "absent" and "present but empty", and rebuildIndexes + // already reconstructs the map without them. let userKey = Principal.toText(order.owner); switch (Map.get(store.openOrdersByUser, Text.compare, userKey)) { case null {}; case (?s) { ignore Map.delete(s, Nat.compare, order.id); - Map.add(store.openOrdersByUser, Text.compare, userKey, s); + if (Map.size(s) == 0) { + ignore Map.delete(store.openOrdersByUser, Text.compare, userKey); + } else { + Map.add(store.openOrdersByUser, Text.compare, userKey, s); + }; }; }; @@ -277,6 +316,38 @@ module { }; }; + // Rebuild ONLY the price-level index from master data. O(open orders × log) + // — bounded by the resting book, never by the trade tape, so unlike the full + // rebuildIndexes it is safe inside an upgrade. Built as the worker for the + // retired one-shot (timestamp, id) re-key migration (applied to every + // target 2026-08-07); kept because the unit suite pins its reconstruction + // and any future level-map migration will need exactly this. + public func rebuildLevelIndex(store : OrderStore) { + Map.clear(store.levelsByMarketSide); + for ((_, o) in Map.entries(store.orders)) { + if (isOpen(o)) { + let msKey = marketSideKey(o.marketId, o.side); + let lvls = switch (Map.get(store.levelsByMarketSide, Text.compare, msKey)) { + case null { + let m = Map.empty>(); + Map.add(store.levelsByMarketSide, Text.compare, msKey, m); + m; + }; + case (?m) { m }; + }; + let lvl = switch (Map.get(lvls, Nat.compare, o.price)) { + case null { + let s = Map.empty<(Int, Nat), Bool>(); + Map.add(lvls, Nat.compare, o.price, s); + s; + }; + case (?s) { s }; + }; + Map.add(lvl, levelKeyCompare, (o.timestamp, o.id), true); + }; + }; + }; + // ── Core operations ──────────────────────────────────────────── // Allocate a fresh order id WITHOUT creating an order. Used by the sealed @@ -438,9 +509,13 @@ module { }; label scan for ((_price, lvl) in levelIter) { // Within a level all orders share the price → pure time priority - // (earliest timestamp, id as tie-break). Skip excluded/closed ids. - var best : ?Types.Order = null; - for ((id, _) in Map.entries(lvl)) { + // (earliest timestamp, id as tie-break) — which is the inner set's KEY + // ORDER, so the first non-excluded open entry IS the level's best. + // Cost: O(log K + entries skipped), not the O(K) full-level walk that + // made a taker sweeping K same-price makers quadratic. Skipped entries + // are excluded ids (pending-locked / non-takeable / self makers the + // engine already passed over) plus the defensive closed/missing check. + for (((_ts, id), _) in Map.entries(lvl)) { let excluded = switch (excludeIds) { case null { false }; case (?set) { Option.isSome(Map.get(set, Nat.compare, id)) }; @@ -448,22 +523,11 @@ module { if (not excluded) { switch (Map.get(store.orders, Nat.compare, id)) { case null {}; - case (?o) { - if (isOpen(o)) { - switch (best) { - case null { best := ?o }; - case (?b) { - if (o.timestamp < b.timestamp or (o.timestamp == b.timestamp and o.id < b.id)) { - best := ?o; - }; - }; - }; - }; - }; + case (?o) { if (isOpen(o)) { return ?o } }; }; }; }; - switch (best) { case (?o) { return ?o }; case null {} }; // level empty/excluded → next + // level exhausted (all excluded/closed) → next-best price }; null; }; @@ -513,6 +577,17 @@ module { }; }; + // Number of open orders resting at ONE price on one market-side — O(log N) + // read of the maintained level aggregate. Used by the per-level placement + // cap (Types.MAX_ORDERS_PER_PRICE_LEVEL): bounding K at a price bounds the + // work of every path that sweeps a level. + public func getLevelOrderCount(store : OrderStore, marketId : Types.MarketId, side : Types.Side, price : Nat) : Nat { + switch (Map.get(store.levelAggByMarketSide, Text.compare, marketSideKey(marketId, side))) { + case null { 0 }; + case (?lvls) { Option.get(Map.get(lvls, Nat.compare, price), (0, 0)).1 }; + }; + }; + // Oldest resting order (order ids are monotonic, so the smallest open id // is the earliest-placed). Used by the per-user book cap's eviction. public func getOldestOpenOrderId(store : OrderStore, user : Principal) : ?Nat { diff --git a/src/backend/lib/PriceFeed.mo b/src/backend/lib/PriceFeed.mo index 274fc30..a786f10 100644 --- a/src/backend/lib/PriceFeed.mo +++ b/src/backend/lib/PriceFeed.mo @@ -190,20 +190,98 @@ module { }; }; - // The samples robustMedian keeps: everything within ±sigmaTrim·stddev of - // the initial median. Under 3 samples (or a degenerate stddev) there's - // nothing to trim against — the full set comes back; a trim that would + // Median absolute deviation — the median of |x − median(x)|. null on an + // empty set. + // + // This is the dispersion estimate the trim bands against, and the reason is + // its BREAKDOWN POINT: half the sample can be arbitrarily corrupted before + // MAD can be dragged anywhere, so no single rogue reading inflates it at any + // magnitude. The sample standard deviation has a breakdown point of zero — + // one bad sample moves it without limit — which is fatal for a detector that + // uses its own dispersion estimate to decide what to reject. + public func mad(xs : [Float]) : ?Float { + let m = switch (median(xs)) { case null { return null }; case (?v) { v } }; + var devs : [Float] = []; + for (x in xs.vals()) { devs := appendFloat(devs, Float.abs(x - m)) }; + median(devs); + }; + + // Makes MAD a consistent estimator of σ for a normal sample, so `sigmaTrim` + // keeps its plain "how many sigmas" meaning at the call site. + public let MAD_TO_SIGMA : Float = 1.4826; + + // MAD × 1.4826 is unbiased for σ only ASYMPTOTICALLY; at the sample sizes an + // oracle fleet actually runs (3-8) it reads systematically LOW — around 50% + // low at n=3. An under-estimated scale is a narrow band and an over-eager + // trim, and over-trimming is not a harmless direction here: every rejected + // sample costs a source against the MIN_ROBUST_SOURCES floor, so a trim that + // is too keen freezes the mark just as effectively as one that is too blind. + // Standard Croux-Rousseeuw finite-sample factors, with the usual n/(n−0.8) + // asymptote past the tabulated range. + func madFiniteSample(n : Nat) : Float { + if (n <= 2) { 1.196 } + else if (n == 3) { 1.495 } + else if (n == 4) { 1.363 } + else if (n == 5) { 1.206 } + else if (n == 6) { 1.200 } + else if (n == 7) { 1.140 } + else if (n == 8) { 1.129 } + else if (n == 9) { 1.107 } + else { let f = Float.fromInt(n); f / (f - 0.8) }; + }; + + // Minimum trim-band half-width, in bps of the median. See trimOutliers. + // + // Set to the caller's own dispersion tolerance (main.mo's + // PRICE_MAX_STDDEV_BPS, 50 bps): a reading closer to the mark than that is + // inside the policy the caller has already declared acceptable, so rejecting + // it would be this module substituting a stricter opinion for the caller's. + // If that gate is ever retuned, retune this with it. + public let TRIM_BAND_FLOOR_BPS : Float = 50.0; + + // The samples robustMedian keeps: everything within ±sigmaTrim·σ̂ of the + // initial median, where σ̂ is estimated from the MAD. Under 3 samples there + // is nothing to trim against — the full set comes back; a trim that would // discard EVERYTHING also returns the full set (all-outliers means "no // agreement", and the caller's quality floor should judge that on the // honest, untrimmed dispersion rather than an empty set). + // + // WHY NOT THE STANDARD DEVIATION. This banded against ±sigmaTrim·stddev of + // the whole set, outlier included, and that is textbook masking: at small n + // the outlier inflates the very band meant to exclude it, by more than its + // own distance from the median. Work n=3, samples (a, a, b), d = |b − a|: + // sd = d/√3, so a 2σ band is ±1.1547·d while the outlier sits at exactly d — + // strictly inside, and the relation is homogeneous in d, so a 0.1% outlier + // and a 100% outlier are BOTH kept. At n=4, (a, a, a, b) gives sd = d/2 and + // a band edge of exactly ±d, landing on the outlier, which the inclusive + // keep test then admits. Rejection only began at n=5. Since a source + // dropping in and out puts the fleet at 3-4 routinely, the trim was + // inoperative in normal operation — and because the caller computes its + // dispersion gate over the KEPT set, a single venue ~1% off the cluster + // blew that gate and froze the mark instead of being rejected. Raising the + // source floor does not help: it moves the failure from "trim never runs" + // to "trim runs and cannot reject". The floor was never the defect; the + // estimator was. public func trimOutliers(xs : [Float], sigmaTrim : Float) : [Float] { let n = xs.size(); if (n < 3) { return xs }; let m = switch (median(xs)) { case null { return xs }; case (?v) { v } }; - let sd = switch (stddev(xs)) { case null { return xs }; case (?v) { v } }; - if (sd < 0.0000001) { return xs }; - let lo = m - sigmaTrim * sd; - let hi = m + sigmaTrim * sd; + let d = switch (mad(xs)) { case null { return xs }; case (?v) { v } }; + // Floor the band width. MAD is exactly 0 whenever more than half the + // samples share a value — three venues printing 2.27, 2.27, 2.31 is the + // ordinary case, not a contrived one — and a zero-width band would trim + // every sample not exactly on the median, good ones included. The floor is + // a fraction of the mark rather than an absolute so it scales across + // assets, and it only ever WIDENS the band, so it can admit a sample the + // MAD would have rejected but can never reject one the MAD would have + // kept. See TRIM_BAND_FLOOR_BPS for why it is set where it is. + let half = Float.max( + sigmaTrim * MAD_TO_SIGMA * madFiniteSample(n) * d, + m * TRIM_BAND_FLOOR_BPS / 10000.0, + ); + if (half <= 0.0) { return xs }; + let lo = m - half; + let hi = m + half; var kept : [Float] = []; for (x in xs.vals()) { if (x >= lo and x <= hi) { kept := appendFloat(kept, x) }; @@ -220,18 +298,63 @@ module { median(trimOutliers(xs, sigmaTrim)); }; + // ── Robustness floor: how many samples it takes to MOVE a mark ───── + // + // Below three surviving readings the aggregate has NO defence against a + // single bad one, and it is worth being precise about why, because the + // obvious answer is wrong: + // + // * `trimOutliers` is not the defence, even though it now works at n≥3. + // It returns the sample set untouched below that (nothing to trim + // against), and a trim can only ever reject a MINORITY — it locates the + // cluster with a median and a MAD, both of which need the good readings + // to be the majority. Give it two samples and there is no majority to + // find. So the trim sharpens a robust aggregate; it cannot make a + // non-robust one robust. + // + // * The MEDIAN is the defence, and what protects it is its breakdown + // point — the fraction of arbitrarily-corrupted samples it tolerates + // before the output can be dragged anywhere the attacker likes. For n + // samples that is floor((n-1)/2)/n: at n=3 one source can be arbitrarily + // wrong and the median still lands on a good sample. At n=2 it is + // EXACTLY ZERO — the median of two is their mean, so a single rogue or + // stale venue moves the mark by half its error, without limit and + // without ever being trimmed. + // + // So n=3 is not a "nicer" sample size, it is the smallest one at which the + // aggregate is robust at all. A caller may still HOLD a mark on two sources + // (a stale-but-corroborated price is fine, and refusing to hold would hand a + // liquidation cascade to whoever can knock a provider offline); what it must + // not do is MOVE the mark on them. + public let MIN_ROBUST_SOURCES : Nat = 3; + + // True iff a sample count is robust enough to move a mark. Callers gate mark + // MOVEMENT on this — see the breakdown-point note above. + public func isRobustSourceCount(n : Nat) : Bool { n >= MIN_ROBUST_SOURCES }; + + // Same test over a sample set. + public func isRobustSample(xs : [Float]) : Bool { isRobustSourceCount(xs.size()) }; + + // Same test over a finished aggregate. `sourceCount` is the count of readings + // that SURVIVED filtering, which is the set the median was actually taken + // over — the right denominator for the breakdown-point argument. + public func canMoveMark(agg : Aggregate) : Bool { isRobustSourceCount(agg.sourceCount) }; + // Aggregate a fleet of readings into a price + quality signal. BOTH the // price and the dispersion are computed over the OUTLIER-TRIMMED sample - // set (±2σ of the initial median). They must share the sample set: one - // venue printing an off-cluster price (thin market, stale cache) used to - // blow `stddevBps` past the caller's quality gate and veto the aggregate - // WHOLESALE — while the robust median being vetoed had already excluded - // that very venue. (Live incident: 7 ICP sources, six at $2.27–2.279 and - // one at $2.31 → untrimmed stddev 58bps > the 50bps gate → refPrice frozen - // for hours on a perfectly priceable market.) `sourceCount` now matches - // its declared contract — readings that SURVIVED filtering — so the - // ≥minSources floor judges the trimmed set too. The full raw fleet stays - // in `readings` for observability. + // set (±2σ̂ of the initial median, σ̂ from the MAD). They must share the + // sample set: one venue printing an off-cluster price (thin market, stale + // cache) used to blow `stddevBps` past the caller's quality gate and veto + // the aggregate WHOLESALE — while the robust median being vetoed had + // already excluded that very venue. (Live incident: 7 ICP sources, six at + // $2.27–2.279 and one at $2.31 → untrimmed stddev 58bps > the 50bps gate → + // refPrice frozen for hours on a perfectly priceable market.) That the + // dispersion is measured on the SURVIVING cluster is the load-bearing half: + // reporting it over cluster-plus-outlier hands the gate the very reading the + // trim exists to discard, which reinstates the freeze the trim prevents. + // `sourceCount` likewise matches its declared contract — readings that + // SURVIVED filtering — so the ≥minSources floor judges the trimmed set too. + // The full raw fleet stays in `readings` for observability. public func aggregate(asset : Asset, readings : [Reading], now : Int) : Aggregate { var okPrices : [Float] = []; for (r in readings.vals()) { @@ -268,6 +391,20 @@ module { // Parse a leading signed decimal from `t`, skipping leading whitespace. // Stops at the first non-numeric character. Returns null if no digits. + // + // SCIENTIFIC NOTATION IS REFUSED, NOT TRUNCATED. A plain "stop at the first + // non-numeric character" loop treats the `e` of `1.5e3` as a terminator and + // silently returns the MANTISSA — 1.5 for a true value of 1500 (1000× low), + // 1.2 for 1.2e-8 (10^8 high). This value becomes the oracle mark that drives + // liquidations and collateral valuation, so a silently wrong magnitude is far + // more dangerous than a missing reading: the aggregator already tolerates a + // source returning nothing (it drops out of the sample and the remaining + // sources carry the mark), but nothing downstream can detect a plausible- + // looking price that is off by three orders of magnitude. In well-formed JSON + // a digit run can only be followed by `,` `}` `]` `"` or whitespace, so an + // `e`/`E` immediately after digits is unambiguously an exponent — refusing it + // costs us nothing on the plain-decimal bodies every wired source emits, and + // makes a truncated magnitude impossible rather than merely unlikely. public func parseLeadingFloat(t : Text) : ?Float { var intPart : Nat = 0; var fracPart : Nat = 0; @@ -276,6 +413,7 @@ module { var sawDigit = false; var negative = false; var sawNonWs = false; + var sawExp = false; label lp for (c in t.chars()) { if (c == ' ' or c == '\t' or c == '\n' or c == '\r') { if (sawNonWs) { break lp }; @@ -294,11 +432,15 @@ module { sawDigit := true; } else if (c == '.' and not sawDot and sawDigit) { sawDot := true; + } else if ((c == 'e' or c == 'E') and sawDigit) { + sawExp := true; // exponent: refuse the whole token (see above) + break lp; } else { break lp; }; }; }; + if (sawExp) { return null }; if (not sawDigit) { return null }; var result : Float = Float.fromInt(intPart); if (fracLen > 0) { @@ -311,53 +453,119 @@ module { ?result; }; - // First occurrence of `needle` in `haystack`; returns the substring - // after it, or null if not found. + // First occurrence of `needle` in `haystack`; returns EVERYTHING after it, + // or null if not found. + // + // The rejoin matters. `Text.split` on a needle that occurs N times yields + // N+1 pieces, so taking the second piece returns only the text BETWEEN the + // first and second occurrence — not the remainder. That was invisible while + // every caller did nothing but parse a leading number out of the result + // (identical either way), but `numberAfterPath` chains this call: an + // intermediate segment that truncated at its own second occurrence would cut + // the target key out of the text before the next segment ever looked for it. + // Re-joining the tail with the needle restores the substring this function's + // name and docstring always claimed to return. public func findAfter(haystack : Text, needle : Text) : ?Text { let parts = Text.split(haystack, #text needle); ignore parts.next(); - parts.next(); + switch (parts.next()) { + case null { null }; + case (?first) { + var rest = first; + for (p in parts) { rest #= needle # p }; + ?rest; + }; + }; }; // Numeric value following `key` in a JSON-ish body, tolerant of the // punctuation between key and value: arbitrary whitespace around the - // colon and an optionally-quoted value. Handles both the compact - // (`"close":64046.03`, `"a":"2.29"`) and pretty-printed (`"a" : "2.29"`) - // bodies providers emit — Crypto.com's gateway pretty-prints, and a - // fixed `"key":"` needle would silently stop matching if a provider - // flipped formatting. + // colon, an optionally-quoted value, and an array wrapper (Kraken's + // `"c":["64046.03","0.1"]`, where the first element is the last trade). + // Handles both the compact (`"close":64046.03`, `"a":"2.29"`) and + // pretty-printed (`"a" : "2.29"`) bodies providers emit — Crypto.com's + // gateway pretty-prints, and a fixed `"key":"` needle would silently stop + // matching if a provider flipped formatting. + // + // FIRST-OCCURRENCE, BY CONSTRUCTION: `findAfter` anchors on the first match + // of `key` in the WHOLE body, so a short key matches any earlier field that + // happens to share its name — `numberAfterKey("{\"a\":\"99.90\",\"ticker\": + // {\"a\":\"2.29\"}}", "\"a\"")` reads 99.90, not the ticker's 2.29. Callers + // that cannot prove their key is unique across the entire body must use + // `numberAfterPath` and name the containing object. public func numberAfterKey(haystack : Text, key : Text) : ?Float { switch (findAfter(haystack, key)) { case null { null }; case (?rest) { let value = Text.trimStart(rest, #predicate (func(c : Char) : Bool { - c == ' ' or c == '\t' or c == '\n' or c == '\r' or c == ':' or c == '\"' + c == ' ' or c == '\t' or c == '\n' or c == '\r' or c == ':' or c == '\"' or c == '[' })); parseLeadingFloat(value); }; }; }; + // Numeric value at a nested PATH: every segment but the last is located in + // the remainder left by the segment before it, so the final (often very + // short) key is only ever searched for INSIDE the object its path names. + // `numberAfterPath(body, ["\"ticker\"", "\"a\""])` reaches the ticker's own + // "a" even when an unrelated `"a"` appears earlier in the body — which plain + // `numberAfterKey` cannot do at any length of key. + // + // Every extractor below is built on this, not on a bare key. A key short + // enough to be cheap to match (`"a"`, `"c"`, `"last"`, `"price"`) is also + // short enough to collide, and the failure is SILENT: the wrong field parses + // perfectly and becomes a price. Anchoring converts that class of upstream + // schema drift from "wrong mark" into "no reading" — the source drops out, + // the aggregate carries on with the rest, and `parse failed` shows up in the + // per-source diagnostics where an operator can see it. + public func numberAfterPath(haystack : Text, path : [Text]) : ?Float { + let n = path.size(); + if (n == 0) { return null }; + var rest = haystack; + var i = 0; + while (i + 1 < n) { + switch (findAfter(rest, path[i])) { + case null { return null }; // container absent ⇒ not the document we expect + case (?r) { rest := r }; + }; + i += 1; + }; + numberAfterKey(rest, path[n - 1]); + }; + + // Pull the price out of one source's response body. + // + // Every WIRED extractor names the CONTAINING OBJECT of the field it wants and + // reads the field from inside it (`numberAfterPath`). The old bare-key form + // took the first match anywhere in the body, which made each extractor's + // correctness rest on an ordering accident — "no other key in the body + // contains `"a"`" is a property of today's response, not of the contract, and + // an upstream field added ABOVE the container silently re-points the parse at + // a different number. Anchoring makes the container part of the match, so + // drift produces null (source drops out, aggregate survives) instead of a + // confident wrong price. The comments record the real body shapes, captured + // live 2026-07-11 and pinned by tests/PriceFeed.test.mo. public func extractFromBody(kind : SourceKind, body : Blob, asset : Asset) : ?Float { - let _ = asset; // reserved for per-asset dispatch in future extractors let text = switch (Text.decodeUtf8(body)) { case null { return null }; case (?t) { t }; }; switch (kind) { case (#coinbase) { - switch (findAfter(text, "\"amount\":\"")) { - case null { null }; - case (?rest) { parseLeadingFloat(rest) }; - }; + // {"data":{"amount":"75517.39","base":"BTC","currency":"USD"}} + numberAfterPath(text, ["\"data\"", "\"amount\""]); }; case (#coingecko) { - switch (findAfter(text, "\"usd\":")) { - case null { null }; - case (?rest) { parseLeadingFloat(rest) }; - }; + // {"bitcoin":{"usd":75490}} — the outer key is the coin id we asked + // for (`ids=bitcoin`), so anchoring on it does double duty: it scopes + // the 5-char "usd" to the right object AND confirms the body answers + // the asset we requested rather than a cached neighbour. + numberAfterPath(text, ["\"" # assetSymbol(#coingecko, asset) # "\"", "\"usd\""]); }; case (#coinpaprika) { + // NOT WIRED (see PRICE_SOURCES in main.mo) — left as-is deliberately. + // {"id":"btc-bitcoin",…,"quotes":{"USD":{"price":X.XX,…}}} switch (findAfter(text, "\"USD\":{\"price\":")) { case null { switch (findAfter(text, "\"price\":")) { @@ -369,28 +577,27 @@ module { }; }; case (#krakenLike) { - // {"result":{"PAIRUSD":{"c":["X.XX","VOL"], ...}}} - switch (findAfter(text, "\"c\":[\"")) { - case null { null }; - case (?rest) { parseLeadingFloat(rest) }; - }; + // {"error":[],"result":{"PAIRUSD":{"a":[…],"b":[…],"c":["X.XX","VOL"],…}}} + // — c[0] is the last trade. Anchor under "result" so a `"c"` inside an + // "error" array (which PRECEDES result, and carries free-form provider + // text) can never be the match; numberAfterKey steps over the `["`. + numberAfterPath(text, ["\"result\"", "\"c\""]); }; case (#okx) { - // {"code":"0","data":[{"instId":"ICP-USDT","last":"2.779", ...}]} - switch (findAfter(text, "\"last\":\"")) { - case null { null }; - case (?rest) { parseLeadingFloat(rest) }; - }; + // {"code":"0","msg":"","data":[{"instId":"ICP-USDT","last":"2.779",…}]} + // — anchor under "data" so the envelope can grow fields freely. The + // needle carries its closing quote, so "lastSz" is not a match. + numberAfterPath(text, ["\"data\"", "\"last\""]); }; case (#kucoin) { - // {"code":"200000","data":{"time":..,"price":"2.777", ...}} — first - // "price" is the last-trade price (before bestBid/bestAsk). - switch (findAfter(text, "\"price\":\"")) { - case null { null }; - case (?rest) { parseLeadingFloat(rest) }; - }; + // {"code":"200000","data":{"time":…,"price":"2.777","size":…, + // "bestBid":"2.777","bestAsk":"2.779"}} — data.price is the last + // trade; it precedes bestBid/bestAsk INSIDE data, and anchoring on + // "data" keeps the envelope out of the search. + numberAfterPath(text, ["\"data\"", "\"price\""]); }; case (#cryptocompare) { + // NOT WIRED (see PRICE_SOURCES in main.mo) — left as-is deliberately. // {"USD":2.774} — value is a bare number, not a quoted string. switch (findAfter(text, "\"USD\":")) { case null { null }; @@ -398,22 +605,29 @@ module { }; }; case (#htx) { - // {"status":"ok","tick":{"open":63167.0,"close":64046.03, ...}} — - // "close" is the last trade price, a bare number, and appears once - // (only inside tick). Error bodies have no "close" → null → ok=false. - numberAfterKey(text, "\"close\""); + // {"ch":"market.icpusdt.detail.merged","status":"ok","ts":…, + // "tick":{"open":63167.0,"close":64046.03,…}} — tick.close is the last + // trade, a bare number. Anchoring on "tick" makes "appears only inside + // tick" structural instead of observed. Error bodies carry no "tick" → + // null → ok=false, as before. + numberAfterPath(text, ["\"tick\"", "\"close\""]); }; case (#cryptocom) { // {"result":{"data":[{"i":"BTC_USDT","h":"…","l":"…","a":"64084.20",…}]}} - // — "a" is the latest trade price. The gateway pretty-prints - // (`"a" : "64084.20"`), which numberAfterKey absorbs. No other key or - // value in the body contains the 3-char sequence `"a"`, so the first - // hit is the price. - numberAfterKey(text, "\"a\""); + // — data[0].a is the latest trade. The gateway pretty-prints + // (`"a" : "64084.20"`), which numberAfterKey absorbs. `"a"` is THE + // pathological short key: three characters, and the old bare-key form + // survived only because nothing above it in the body happened to + // contain them. Anchored under result → data, it cannot. + numberAfterPath(text, ["\"result\"", "\"data\"", "\"a\""]); }; case (#binance) { - // {"symbol":"BTCUSDT","price":"64158.01000000"} — the only "price". - numberAfterKey(text, "\"price\""); + // {"symbol":"BTCUSDT","price":"64158.01000000"} — a flat two-key + // document with no container to anchor to, so anchor on the sibling + // that always precedes the price. That also rejects any OTHER body + // that happens to carry a "price" (e.g. an error envelope) as not + // being the ticker document. + numberAfterPath(text, ["\"symbol\"", "\"price\""]); }; }; }; diff --git a/src/backend/lib/Types.mo b/src/backend/lib/Types.mo index 388c6fd..78bfa1f 100644 --- a/src/backend/lib/Types.mo +++ b/src/backend/lib/Types.mo @@ -274,6 +274,22 @@ module { // dust balances must never be stranded/unsellable. public let MIN_ORDER_ICPUSD : Nat = 1_000_000_000; // 10.0 ICPUSD + // Max open orders resting at ONE price on one market-side. A congestion + // bound, not an economic one: every path that sweeps a level (a large + // taker, the AMM sweep, liquidation sells, deferred-expiry releases) does + // work proportional to the level's order count per message, so the count + // must not be attacker-unbounded. 512 sits well under the ~3k same-price + // orders measured to trap the 40B instruction limit pre-fix, and far above + // organic congestion at magnet prices (the per-user open-order cap is 100). + // REJECT-new, never evict: eviction would let a spammer displace honest + // makers' queue positions, while rejection denies only this exact price — + // one tick away is always free on the 10^-8 grid. Enforced at STAGING + // (LiquidityManager.validateNewOrder), so a burst staged before any of it + // rests can overshoot the cap by the in-flight staged cohort (bounded by + // the global staged shed, ~2k); the engine's per-call iteration cap is what + // makes any overshoot harmless. + public let MAX_ORDERS_PER_PRICE_LEVEL : Nat = 512; + // Combined cross-market bid exposure cap = CROSS_MARKET_BID_FACTOR × cash. public let CROSS_MARKET_BID_FACTOR : Nat = 300_000_000; // 3.0 diff --git a/src/backend/main.mo b/src/backend/main.mo index 735169c..67ae165 100644 --- a/src/backend/main.mo +++ b/src/backend/main.mo @@ -58,19 +58,28 @@ import Expose "oql/Expose"; import OqlJson "oql/Json"; // OQL query-JSON parser (for the History proxy; distinct from mo:json) import Json "mo:json"; // request-body build + response parse for the AI proxy -// NOTE: TWO one-shot EOP migrations have been applied to the live canister and -// are RETIRED here: dropping `nextDeferredId` (2026-06-01, git 684124e→6ed3362) -// and dropping the legacy stable `IS_PRODUCTION` field after the flag became -// `transient` (2026-06-10). A consume-the-field migration runs only on upgrade -// and must be removed once applied — re-running it traps ("stable variable … -// expected but not found") because the field is already gone. New code and the -// migrated state both lack the fields, so the plain upgrade is compatible. +// NOTE: THREE one-shot EOP migrations have been applied to the live canister +// and are RETIRED here: dropping `nextDeferredId` (2026-06-01, git +// 684124e→6ed3362), dropping the legacy stable `IS_PRODUCTION` field after +// the flag became `transient` (2026-06-10), and re-keying the order book's +// price-level index by (timestamp, id) (migration.mo, 2026-08-07 — applied +// to local, the cloud engine, and the subnet the same day; OrderBook's +// rebuildLevelIndex was its worker and remains). A one-shot migration runs +// only on upgrade and must be removed once applied — re-running a +// consume-the-field one traps ("stable variable … expected but not found"), +// and a transform one refuses the upgrade outright because the stored state +// no longer matches its domain. New code and the migrated state agree, so +// the plain upgrade is compatible. persistent actor Uplands { // ── Deployment posture ─────────────────────────────────────────── // Three first-class postures (docs/deployment-modes.md): // #dev — local replica / CI: open faucet, seeds, the sim, and - // every behaviour/price test hook. + // every behaviour/price test hook. Doctrine (2026-08-06): + // #dev runs the SAME user-facing code as #play — gates, + // caps, binding requirements included — differing ONLY in + // hook availability, so the #dev suite exercises all #play + // logic and #play installs may skip hook-dependent tests. // #play — public play-money competition (e.g. a cloud engine): // users claim ONE fixed starter basket (claimPlayFunds); // the open faucet and every behaviour/price hook — @@ -94,6 +103,15 @@ persistent actor Uplands { transient let IS_PRODUCTION : Bool = DEPLOY_MODE == #production; transient let IS_DEV : Bool = DEPLOY_MODE == #dev; + // The release this wasm was built from — bump together with package.json + // ("version"), which is the frontend's single source (vite bakes it into + // the bundle and /version.json; stale clients poll the latter and refresh + // themselves — src/frontend/src/update-check.js). This constant is the + // DIAGNOSTIC half: getAppVersion / getCanisterInfo.appVersion let anyone + // see which release the backend runs, so a mixed frontend/backend deploy + // is visible at a glance. Transient for the same reason as DEPLOY_MODE. + transient let APP_VERSION : Text = "1.60.0"; + // Runtime TARGET — ORTHOGONAL to DEPLOY_MODE (the posture axis above). It // governs WHO PAYS for cycles, and therefore how many cycles a call attaches // to HTTPS outcalls / archive spawns (see outcallCycles + archiveSpawnCycles): @@ -108,18 +126,6 @@ persistent actor Uplands { public type RuntimeEnv = { #local; #cloudEngine; #subnet }; transient let RUNTIME_ENV : RuntimeEnv = #subnet; - // Display-only mirror of the canister's `wasm_memory_limit` setting — the - // IC gives a canister no API to read its own limit, so getCanisterInfo - // reports this constant and Stats → Canister renders consumption against - // it. Keep in sync whenever ops changes the real setting: - // icp canister settings update backend --wasm-memory-limit - // (Drift is fail-safe: a stale-low value warns early, never late.) - // 2026-06-10: raised 3 GiB → 4 GiB after the order-map leak bricked - // updates; later that day raised to 5.25 GiB alongside shipping the - // closed-order reaper (wasm64 hard wall is 6 GiB — keep a margin so the - // limit stays an early-warning brake, and upgrades have working room). - transient let WASM_MEMORY_LIMIT_BYTES : Nat = 5_637_144_576; - // Max debt $-value the margin-account close flow will settle from the // caller's ICPUSD cash (at the oracle mark) instead of requiring in-kind // repayment — so a borrower who sold the asset and only owes interest dust @@ -374,6 +380,26 @@ persistent actor Uplands { // against user liquidity ONLY (no AMM) so user↔user trading survives an oracle // outage — generous vs the ~1s GEPTOR so a normal fetch always wins first. transient let DEFERRED_EXPIRY_NS : Int = 15_000_000_000; // 15 s + // Max expired entries released in one finalise pass — see processDeferredExpiry. + // Originally sized against a measured trap point (~2,970 orders at ONE price + // level hit the 40B instruction limit) when each release's matcher re-walked + // its price level per fill — an O(K²) term in same-price orders. That + // quadratic is now structurally gone (the level index is keyed by + // (timestamp, id), and the engine caps iterations per call), so this cap's + // remaining job is the per-release fixed overhead: sort, margin clamp, + // rejection records, stats. Kept at 256 — it also bounds how much backlog a + // single pass can dump into one message. + transient let MAX_EXPIRY_RELEASES_PER_PASS : Nat = 256; + // Fill budget for one expiry pass. The release cap above bounds RELEASES, + // and the engine bounds fills PER release (MAX_MATCH_ITERATIONS_PER_CALL), + // but their product — 256 releases × up to 256 fills, each fill paying + // settlement + trade recording + candle upkeep — could still compound past + // the instruction limit in the pathological case (a cohort of whale-sized + // entries into a deep users-only book). Once a pass has settled this many + // fills it stops; unreleased entries stay expired in the queue and the next + // finalise tick (~seconds) continues from the oldest. Nothing is dropped, + // only paced — the same liveness argument as the release cap. + transient let EXPIRY_PASS_FILL_BUDGET : Nat = 1_024; // Anti-free-look: a staged TAKER entry is COMMITTED for this long before its // owner may cancel it. Without this, stage → watch a faster external feed → @@ -610,6 +636,17 @@ persistent actor Uplands { // setAmmAutoInventory(true). var _ammAutoInventory : Bool = false; + // Genesis latch for the #play backdrop window (see injectHistoricalTrades): + // flips true on the first enableAmm(_, true) of this install, never back. + // STABLE on purpose, twice over — it must survive upgrades AND + // performWorldWipe (which clears `pools` itself but deliberately not this), + // so a season reset cannot re-open the pre-launch injection window; only a + // reinstall (a genuinely new venue) re-arms it. An install upgraded from + // before this var existed initializes it false even on a live venue + // (docs/deployment-modes.md, persistence gotcha 1) — postupgrade re-latches + // from any enabled pool that survived the upgrade. + var _ammEverEnabled : Bool = false; + // Auto taker-rebalancer: default OFF. Inventory recovery is passive — the // one-sided skewed ladder refills below the mark / sheds above it and // traders arb holdings back (the Uniswap model: the pool never crosses the @@ -811,15 +848,25 @@ persistent actor Uplands { case null { false }; }; }; + // KEYED ON THE BENEFICIAL OWNER, not the raw principal. openPosition and + // closePosition stage under the POOL principal, and MAX_POOLS_PER_OWNER is + // 64 — so keying the cap on the raw principal gave one account 65 independent + // 32-slot budgets (2,080 staged entries), which is precisely what the cap's + // own comment says it prevents. Worse, tierRankOf already resolves every pool + // back to the owner's level, so all 65 keys sorted at the SAME rank, and 2,080 + // sits above SHED_SOFT_STAGED (2,000) — one account could raise the shed floor + // that then excludes everybody else. scorecardKeyOf is the same key the level + // and fee ladders use; the mismatch between the cap key and the tenancy + // boundary was the bug. func stagedCountOf(owner : Principal) : Nat { - Option.get(Map.get(stagedCountByOwner, Text.compare, Principal.toText(owner)), 0); + Option.get(Map.get(stagedCountByOwner, Text.compare, scorecardKeyOf(owner)), 0); }; func incStagedCount(owner : Principal) { - let k = Principal.toText(owner); + let k = scorecardKeyOf(owner); Map.add(stagedCountByOwner, Text.compare, k, stagedCountOf(owner) + 1); }; func decStagedCount(owner : Principal) { - let k = Principal.toText(owner); + let k = scorecardKeyOf(owner); // must match incStagedCount's key exactly let n = stagedCountOf(owner); if (n <= 1) { ignore Map.delete(stagedCountByOwner, Text.compare, k) } else { Map.add(stagedCountByOwner, Text.compare, k, n - 1 : Nat) }; @@ -922,6 +969,28 @@ persistent actor Uplands { if (lv >= BADGE_WHALE_VOL and not hasBadge(k, BADGE_WHALE)) { awardBadge(k, BADGE_WHALE, now) }; if (lm >= BADGE_PILLAR_VOL and not hasBadge(k, BADGE_PILLAR)) { awardBadge(k, BADGE_PILLAR, now) }; }; + // Drop the MM freshness stamps a staging wrote. EVERY path that cancels a + // staged intent before it lands must call this. + // + // The stamps are written at PLACEMENT and were once cleared nowhere but + // resetExchange, so staging a post-only order and cancelling it immediately + // bought a full quote shield for free: post-only bypasses the 3s + // anti-free-look lock (deferredCommitted returns false for it), the + // reservation is refunded, and isMMShieldedStale is owner-level with a 30s + // TTL — a two-call loop every ~29s held a shield over the maker's entire book + // on every market with no live intent behind it. The shield's whole + // justification is that the maker HAS a staged intent they could requote; an + // intent that never lands must not shield anything. + // + // This is a shared helper rather than two copies of the deletes because that + // is precisely how it went wrong: the clear was added to cancelOwnSpotOrder + // while the public cancelMyOrder kept its own near-identical staged branch, + // and the exploit survived verbatim through the endpoint users actually call. + func clearMmShield(owner : Principal, marketId : Types.MarketId) { + ignore Map.delete(mmQuoteStamp, Text.compare, Principal.toText(owner) # "#" # marketId); + ignore Map.delete(mmOwnerStamp, Text.compare, Principal.toText(owner)); + }; + // Freshness shield checks (see mmQuoteStamp above). Level 4 — the tier whose // qualification IS sustained two-sided quoting — earns the shield. // fresh pass — quotes on `marketId` are shielded while a staged intent @@ -2072,6 +2141,17 @@ persistent actor Uplands { // AMM is never "hit" by takers — it fills on its own terms here. Crossing // orders are snapshotted first so re-rested remainders aren't reprocessed. type SweepItem = { id : Nat; owner : Principal; takerSide : Types.Side; price : Nat; qty : Nat; ts : Int }; + // Snapshot-walk budget per side for ammSweepResting. The collection loops + // below EXCLUDE-and-continue: every order they pass over stays OPEN, so its + // level never shrinks and each subsequent findBestMatchExcluding re-skips + // the whole excluded prefix — collecting N crossing orders costs O(N²) + // entry-skips. Normally N is tiny (crossers are swept every ~2s requote), + // but an oracle stall accumulates them and the recovery sweep would take + // the entire backlog in one heartbeat message. The walk collects best-price + // first, earliest first, so capping it defers exactly the lowest-priority + // tail — to the next requote's sweep, seconds later. + transient let SWEEP_SCAN_MAX_PER_SIDE : Nat = 512; + func ammSweepResting(pool : AMM.Pool, now : Int) { let marketId = pool.marketId; let baseToken = pool.baseToken; @@ -2102,7 +2182,11 @@ persistent actor Uplands { let scan = Map.empty(); for ((k, v) in Map.entries(ammIds)) { Map.add(scan, Nat.compare, k, v) }; if (haveAsk) { + var scanned : Nat = 0; label bids loop { + // Per-side walk budget — see SWEEP_SCAN_MAX_PER_SIDE. + if (scanned >= SWEEP_SCAN_MAX_PER_SIDE) { break bids }; + scanned += 1; switch (OrderBook.findBestMatchExcluding(orderStore, marketId, #sell, ?scan)) { case null { break bids }; case (?o) { @@ -2123,7 +2207,11 @@ persistent actor Uplands { if (haveBid) { let scan2 = Map.empty(); for ((k, v) in Map.entries(ammIds)) { Map.add(scan2, Nat.compare, k, v) }; + var scanned2 : Nat = 0; label asks loop { + // Per-side walk budget — see SWEEP_SCAN_MAX_PER_SIDE. + if (scanned2 >= SWEEP_SCAN_MAX_PER_SIDE) { break asks }; + scanned2 += 1; switch (OrderBook.findBestMatchExcluding(orderStore, marketId, #buy, ?scan2)) { case null { break asks }; case (?o) { @@ -2758,8 +2846,21 @@ persistent actor Uplands { // recurring finaliser regardless of AMM/requote state. Sorted oldest-first. func processDeferredExpiry(now : Int) { let expired = List.empty(); - for ((_, d) in Map.entries(deferredExecs)) { - if (now >= d.expiresAt) { List.add(expired, d) }; + label collect for ((_, d) in Map.entries(deferredExecs)) { + if (now >= d.expiresAt) { + List.add(expired, d); + // CAP THE COHORT. Entries staged in the same window share an expiry + // instant (DEFERRED_EXPIRY_NS is a flat offset from staging), so one + // stalled price-refresh window expires the whole queue at once, and an + // uncapped pass once trapped the 40B instruction limit (measured at + // ~2,970 orders stacked on one price, back when the matcher re-walked + // its level per fill — that quadratic is fixed structurally now; see + // MAX_EXPIRY_RELEASES_PER_PASS). The cap keeps each pass's release + // machinery bounded; the remainder is picked up on the next pass — + // this loop is re-entered every finalise tick, so nothing is dropped, + // only deferred. + if (List.size(expired) >= MAX_EXPIRY_RELEASES_PER_PASS) { break collect }; + }; }; if (List.size(expired) == 0) { return }; let arr = sortDeferredByTs(Iter.toArray(List.values(expired))); @@ -2788,7 +2889,12 @@ persistent actor Uplands { // Group trades/affected by market so stats update per market. let byMarketTrades = Map.empty>(); let affectedSet = Map.empty(); - for (d in arr.vals()) { + // Pass-wide fill budget — see EXPIRY_PASS_FILL_BUDGET. Checked at the top + // so a budget-out also pauses the (cheap) FOK kills; both resume from the + // oldest entry on the next tick. + var passFills : Nat = 0; + label rel for (d in arr.vals()) { + if (passFills >= EXPIRY_PASS_FILL_BUDGET) { break rel }; if (Map.get(deferredFok, Nat.compare, d.id) == ?true) { // Fill-or-kill never got a fresh price in time → KILL + refund. (We must // not partial-fill it against users-only: the FOK guarantee is all-or- @@ -2806,7 +2912,9 @@ persistent actor Uplands { let lst = switch (Map.get(byMarketTrades, Text.compare, d.marketId)) { case (?l) { l }; case null { let l = List.empty(); Map.add(byMarketTrades, Text.compare, d.marketId, l); l }; }; + let fillsBefore = List.size(lst); releaseDeferred(d, usersOnlyCtx, lst, affectedSet); + passFills += SafeMath.subOrZero(List.size(lst), fillsBefore); }; }; let affectedArr = Iter.toArray(Iter.map<(Text, Principal), Principal>(Map.entries(affectedSet), func((_, p)) { p })); @@ -3021,7 +3129,19 @@ persistent actor Uplands { }; for (mid in List.values(due)) { ignore Map.delete(_geptorDeadline, Text.compare, mid); - ignore geptorFetchAndSweep(mid); // async: fetch fresh price → requote → sweep + // SINGLE-FLIGHT per market. The deadline is deleted before the fetch + // fires, and refreshMultiSourcePrice fans out to 8 HTTPS outcalls that + // return only when the slowest completes — routinely longer than the ~1s + // GEPTOR delay, and the file's own notes put non-replicated outcall + // latency 2-10x worse than replicated. Without this guard several chains + // for one market are in flight at once and land in ARBITRARY order, so a + // reading sampled 10s ago can overwrite one sampled 2s ago. It also means + // a degraded oracle INCREASES fan-out exactly when the system is stressed. + if (Option.get(Map.get(_geptorInFlight, Text.compare, mid), false)) { /* already fetching */ } + else { + Map.add(_geptorInFlight, Text.compare, mid, true); + ignore geptorFetchAndSweep(mid); // async: fetch fresh price → requote → sweep + }; }; }; @@ -3032,6 +3152,11 @@ persistent actor Uplands { // we still requote+sweep at the existing price; the periodic price timer is // the fallback. In production the fetch is the HTTPS oracle outcall. func geptorFetchAndSweep(marketId : Types.MarketId) : async () { + // Released in `finally` so an early return OR a thrown error still clears + // the flag — the same idiom tickShipEvents uses. (A trap is not catchable + // in Motoko and would roll this message back wholesale, which also leaves + // the flag unset, since the write is part of the rolled-back message.) + try { switch (Map.get(markets, Text.compare, marketId)) { case null {}; case (?(baseToken, _)) { @@ -3053,6 +3178,7 @@ persistent actor Uplands { processDeferredSwaps(Time.now()); }; }; + } finally { ignore Map.delete(_geptorInFlight, Text.compare, marketId) }; }; func appendNat(xs : [Nat], x : Nat) : [Nat] { @@ -3346,6 +3472,92 @@ persistent actor Uplands { func tickLiquidations() : async () { if (_timersPaused) { return }; runLiquidationBatch(Time.now()); + // Stamp on COMPLETION, not on dispatch. The heartbeat used to assign + // _lastLiqNs := now before calling this, so the schedule stamp committed in + // the parent message even when the batch rolled back — the engine could be + // dead for hours with every stamp advancing normally. Stamping here means a + // batch that never finishes leaves _lastLiqNs frozen, which is the signal. + _lastLiqNs := Time.now(); + _liqCompletions += 1; + // A completed pass is the only evidence the engine works, so it is the only + // thing that clears the streak. Recovery is automatic from here: the next + // dispatch is back on the 30s cadence. + _liqFailStreak := 0; + _liqBreakerLogged := false; + }; + + // Decide whether a liquidation pass is due, maintaining the failure streak + // that paces it. Returns true when the caller should fire tickLiquidations(). + // + // The send itself stays in the heartbeat: `tickLiquidations` is a genuine + // message (moc refuses the call from a plain function for want of send + // capability, M0047 — which is also the proof that a trap inside it rolls + // back only that message rather than the whole beat). + // + // The streak is judged HERE rather than by the callee, because a trapping + // message cannot report its own failure — that is the whole reason the + // counters exist. If the previous dispatch has not reached its completion + // stamp by the time the next is due, it did not finish: at HB_LIQ_NS=30s + // against a batch bounded by LIQ_BATCH_MAX, a pass still in flight after a + // full interval is a trap, not slowness. + func armLiquidationDispatch(now : Int) : Bool { + if (now - _lastLiqDispatchNs < liqDispatchIntervalNs()) { return false }; + if (_liqDispatches > _liqCompletions) { + _liqFailStreak += 1; + // Edge-triggered, not per-retry: a wedged engine backing off toward the + // 32-minute cap would otherwise fill the public log with the same line. + if (_liqFailStreak >= LIQ_FAIL_BREAK_THRESHOLD and not _liqBreakerLogged) { + _liqBreakerLogged := true; + logEvent( + "error", "system", + "Liquidation sweep failing (streak " # Nat.toText(_liqFailStreak) + # ") — backing off; the solvency engine is not running. " + # "Inspect getLiquidationSweepHealth, then adminRunLiquidationBatch.", + null, + ); + }; + } else { + _liqFailStreak := 0; + }; + _lastLiqDispatchNs := now; + _liqDispatches += 1; + true; + }; + + // Is the liquidation sweep actually running? `pending` is dispatches that + // never reached the completion stamp; a persistently non-zero value means the + // batch is trapping and the solvency engine is not running, regardless of how + // healthy the heartbeat looks. `cursor` non-null means a multi-pass sweep is + // mid-epoch, which is normal. + // `failStreak` / `backoffNs` expose the pacing: a non-zero streak means + // passes are failing and retries are being spaced out, and `backoffNs` is the + // interval currently in force (HB_LIQ_NS when healthy). + public query func getLiquidationSweepHealth() : async { + dispatches : Nat; completions : Nat; pending : Nat; + lastCompletedNs : Int; cursorActive : Bool; + failStreak : Nat; backoffNs : Int; + } { + { + dispatches = _liqDispatches; + completions = _liqCompletions; + pending = SafeMath.subOrZero(_liqDispatches, _liqCompletions); + lastCompletedNs = _lastLiqNs; + cursorActive = _liqCursor != null; + failStreak = _liqFailStreak; + backoffNs = liqDispatchIntervalNs(); + }; + }; + + // Clear the backoff after fixing whatever was trapping the batch, so the next + // heartbeat resumes the 30s cadence instead of waiting out the current + // interval. Purely a pacing reset: it starts no pass of its own (use + // adminRunLiquidationBatch for that) and cannot mask a fault, since a streak + // that is still failing simply rebuilds. + public shared (msg) func adminResetLiquidationBreaker() : async () { + requireController(msg.caller); + _liqFailStreak := 0; + _liqBreakerLogged := false; + _lastLiqDispatchNs := 0; }; // ── Margin Phase 3B: cross-market netting ──────────────────── @@ -3381,14 +3593,25 @@ persistent actor Uplands { func userMarksFresh(user : Principal) : Bool { userMarksFreshAt(user, Time.now()) }; func runLiquidationBatch(now : Int) { - // Snapshot loaned users up front — the loan map mutates (entries get - // deleted as debts clear) during netting + book liquidation. - let users = Iter.toArray( - Iter.map<(Text, Map.Map), Text>( - Map.entries(loans), - func((k, _)) { k }, - ) - ); + // Snapshot a BOUNDED SLICE of loaned users, resuming after _liqCursor — the + // loan map mutates (entries get deleted as debts clear) during netting + + // book liquidation, so we take the keys up front either way. The cap is + // what keeps this message inside the instruction limit; see LIQ_BATCH_MAX. + let slice = List.empty(); + let iter = switch (_liqCursor) { + case (?c) { Map.entriesFrom(loans, Text.compare, c) }; + case null { Map.entries(loans) }; + }; + var reachedEnd = true; + label gather for ((k, _) in iter) { + if (_liqCursor == ?k) { continue gather }; // entriesFrom re-emits the cursor key + List.add(slice, k); + if (List.size(slice) >= LIQ_BATCH_MAX) { reachedEnd := false; break gather }; + }; + let users = Iter.toArray(List.values(slice)); + // Advance (or reset) the cursor for the next beat. Resetting on a short + // slice is what makes successive passes cover the whole book. + _liqCursor := if (reachedEnd) { null } else if (users.size() > 0) { ?users[users.size() - 1] } else { null }; // Phase 1 — accrue interest + build a netting plan for every // liquidatable user (classifies each as sell-base / buy-base / @@ -3542,6 +3765,7 @@ persistent actor Uplands { // O(1), additive stable state. See docs/pre-mainnet-checklist.md. let ownerPoolCount = Map.empty(); // ownerText → pools created transient let MAX_POOLS_PER_OWNER : Nat = 64; + transient let MAX_POOL_NAME_LEN : Nat = 64; // ── Position episodes + pool money-flow history ── // An EPISODE is one open→flat lifetime of a position (per pool+market). The @@ -3693,7 +3917,7 @@ persistent actor Uplands { let ltv = switch (Types.marginLTV(baseToken)) { case (?x) { x }; case null { 0 } }; let baseHeld = Accounts.getBalance(accounts, poolP, baseToken); let baseLegUsd = Fixed.mul(Fixed.mul(baseHeld, mark, false), ltv, false); - let otherColl = if (h.collateralUsd > baseLegUsd) { h.collateralUsd - baseLegUsd } else { 0 }; + let otherColl = SafeMath.subOrZero(h.collateralUsd, baseLegUsd); if (size > 0) { MarginPools.liqPriceLong(otherColl, h.debtUsd, size, ltv, Types.MAINTENANCE_HEALTH_RATIO) } else { @@ -4851,6 +5075,10 @@ persistent actor Uplands { // exact semantics of a bridge deposit/withdrawal for any other account). public shared (msg) func fundArbitrageur(amount : Nat) : async { #ok; #err : Text } { requireController(msg.caller); + // Unbacked credit — same interlock every sibling carries. seedInsuranceFund + // states the rule this was breaking: "On #production balances enter only via + // the Bridge … never minted by a controller." + if (IS_PRODUCTION) { return #err("fundArbitrageur is disabled on #production — the arb must be funded through a BACKED path") }; switch (effectiveArb()) { case null { #err("No arbitrage canister wired (setArbitrageur first)") }; case (?p) { @@ -4884,6 +5112,12 @@ persistent actor Uplands { public shared (msg) func donateToVault(amount : Nat, fromTreasury : Bool) : async { #ok; #err : Text } { requireController(msg.caller); if (amount == 0 or amount > 100_000_000_000_000) { return #err("amount must be 0 < a <= $1M e8") }; + // The fromTreasury=true path debits the treasury first and is + // conservation-neutral, so it stays available. The false path is a bare + // mint and must not exist on a value-bearing deployment. + if (IS_PRODUCTION and not fromTreasury) { + return #err("donateToVault without fromTreasury mints unbacked value — disabled on #production; recapitalize from the treasury instead"); + }; let amm = ammPrincipal(); if (fromTreasury) { if (Accounts.getBalance(accounts, treasuryPrincipal(), Types.QUOTE_TOKEN) < amount) { return #err("exceeds treasury balance") }; @@ -4918,6 +5152,11 @@ persistent actor Uplands { public type ArbSide = { #importBase; #exportBase }; public shared (msg) func extMarketSwap(token : Types.TokenId, side : ArbSide, baseAmount : Nat) : async { #ok : Nat; #err : Text } { if (not isArbitrageur(msg.caller)) { return #err("Caller is not the wired arbitrage canister") }; + // #importBase mints synthetic base supply against no custody, and this is + // reachable by a NON-controller (the wired arb canister) — so it is the one + // unbacked-credit path an attacker could reach without the controller key. + // Bounded today only by ARB_MAX_SWAP_USD / ARB_HOURLY_CAP_USD. + if (IS_PRODUCTION) { return #err("extMarketSwap is disabled on #production — synthetic base supply must not be minted against no custody") }; if (baseAmount == 0) { return #err("Amount must be positive") }; let marketId = token # "-" # Types.QUOTE_TOKEN; let pool = switch (AMM.getPool(pools, marketId)) { @@ -5380,14 +5619,16 @@ persistent actor Uplands { }; }; - // The gated resolver for allowance CONSUMERS: on #play an unbound - // principal has no bucket at all — a second principal on the same Google - // account gets nothing, and deposits need a verified identity up front. + // The gated resolver for allowance CONSUMERS: an unbound principal has no + // bucket at all — a second principal on the same Google account gets + // nothing, and deposits need a verified identity up front. The gate holds + // on #dev exactly as on #play (posture doctrine at DEPLOY_MODE): tests + // satisfy it through setTestEmailBinding, which is the one #dev-only part. func playBucketFor(user : Principal) : { #ok : PlayBucket; #err : Text } { switch (playBucketOf(user)) { case (#email(eh)) { #ok(#email(eh)) }; case (#principal(pk)) { - if (DEPLOY_MODE == #play) { + if (DEPLOY_MODE != #production) { #err("Deposits require a verified Google-linked Internet Identity — open the Deposit page and press \"Verify with Google\" first (one funded account per player keeps the competition honest)"); } else { #ok(#principal(pk)) }; }; @@ -5411,7 +5652,7 @@ persistent actor Uplands { func playDepositCap() : ?Nat { switch (_testPlayDepositCap) { case (?c) { ?c }; - case null { switch (DEPLOY_MODE) { case (#play) { ?PLAY_DEPOSIT_CAP_USD }; case _ { null } } }; + case null { switch (DEPLOY_MODE) { case (#production) { null }; case _ { ?PLAY_DEPOSIT_CAP_USD } } }; }; }; @@ -5456,7 +5697,7 @@ persistent actor Uplands { // SHA-256 lands in state, and the salt is a private stable secret. let emailBindings = Map.empty(); // salted email-hash → first-bound principal let principalEmail = Map.empty(); // principalText → salted email-hash - let playDepositUsedByEmail = Map.empty(); // salted email-hash → e8 USD consumed (survives resets) + let playDepositUsedByEmail = Map.empty(); // salted email-hash → e8 USD consumed (survives resetExchange; resetSeason re-arms it) var emailSalt : Blob = ""; // private stable secret; raw_rand once, never exposed transient let bindErrors = Map.empty(); // principalText → last bind failure (UX surface only) @@ -5506,11 +5747,13 @@ persistent actor Uplands { out; }; - // The shared binding core (mixin callback + non-prod test hook). First-come: - // one email ↔ one principal, no self-service rebinds (support/admin path - // only — a rebind mints a fresh allowance otherwise). On success, any - // allowance the principal consumed BEFORE binding folds into the email - // bucket so existing play users keep their history. + // The shared binding core (mixin callback + #dev-only test hook). First-come: + // one email ↔ one principal, and NO rebind path — by design, not omission + // (owner decision 2026-08-06): a rebind would mint a fresh allowance, and a + // player who loses the Google account or II anchor simply rejoins with a + // new one (play money, competition stakes only). On success, any allowance + // the principal consumed BEFORE binding folds into the email bucket so + // existing play users keep their history. func bindVerifiedEmail(caller : Principal, rawEmail : Text) { let ck = Principal.toText(caller); ignore Map.delete(bindErrors, Text.compare, ck); @@ -5580,7 +5823,7 @@ persistent actor Uplands { let ck = Principal.toText(msg.caller); { bound = Map.get(principalEmail, Text.compare, ck) != null; - required = DEPLOY_MODE == #play; + required = DEPLOY_MODE != #production; lastError = Map.get(bindErrors, Text.compare, ck); }; }; @@ -5590,7 +5833,16 @@ persistent actor Uplands { // bucket accounting, and conflict rules are integration-testable. public shared (msg) func setTestEmailBinding(user : Principal, rawEmail : Text) : async { #ok; #err : Text } { requireController(msg.caller); - if (IS_PRODUCTION) { return #err("Test bindings are disabled on production") }; + // #dev ONLY, like every sibling test hook (setTestScorecard, setAmmRefPrice, + // debugInspectByUsername). Gating on IS_PRODUCTION left it LIVE on #play, + // where bindVerifiedEmail accepts any well-formed string with no Google + // round-trip — so it minted anti-Sybil identities. And because binding is + // first-come with NO rebind path anywhere in this file (by design — see + // the binding-core comment above bindVerifiedEmail), calling it + // against a victim's principal permanently blocks that victim's real + // verification. The operator-fairness rule this family exists under says the + // operator must not be able to do this on a live venue. + requireDevHook("setTestEmailBinding"); if (emailSalt.size() == 0) { await ensureEmailSalt() }; bindVerifiedEmail(user, rawEmail); switch (Map.get(bindErrors, Text.compare, Principal.toText(user))) { @@ -5915,6 +6167,47 @@ persistent actor Uplands { transient var _lastReapNs : Int = 0; transient var _lastShipNs : Int = 0; transient var _lastLiqNs : Int = 0; + // Resume point for the sharded liquidation sweep (null = start a fresh pass). + var _liqCursor : ?Text = null; + // Dispatch/completion counters. A trap inside tickLiquidations rolls that + // message back silently — no stamp regresses, no counter freezes, and the + // canister looks healthy while its solvency engine is dead. These two make it + // observable: the heartbeat bumps _liqDispatches BEFORE the call, the batch + // bumps _liqCompletions only after finishing, so a persistent gap is proof + // the batch is failing. Surfaced by getLiquidationSweepHealth(). + var _liqDispatches : Nat = 0; + var _liqCompletions : Nat = 0; + // Consecutive dispatches that were still incomplete when the NEXT one came + // due. Counters alone only make the failure visible; they do not stop it, and + // stamping _lastLiqNs on completion turned that from an omission into a + // hazard. Once the batch traps, _lastLiqNs stops advancing, so the dispatch + // predicate below stays true FOREVER and re-fires on every heartbeat instead + // of every 30s — an unthrottled retry of a message that is guaranteed to trap + // and guaranteed to bill for the instructions it burns before trapping. So + // the cadence is measured from DISPATCH (which always advances) while + // _lastLiqNs stays the completion-only health signal, and the streak backs + // the retry off geometrically instead of hammering. + // + // Backoff, not a hard halt. A halt is the wrong default on a solvency path: + // it needs an operator to notice, and if nobody does, liquidations never + // resume. Backoff bounds the burn, keeps the venue trying, and recovers by + // itself the moment a pass completes. adminRunLiquidationBatch remains the + // manual override, and adminResetLiquidationBreaker clears the streak for an + // operator who has fixed the cause and wants the 30s cadence back at once. + transient var _liqFailStreak : Nat = 0; + transient var _lastLiqDispatchNs : Int = 0; + transient var _liqBreakerLogged : Bool = false; + transient let LIQ_FAIL_BREAK_THRESHOLD : Nat = 3; // ~90s at HB_LIQ_NS=30s before backing off + transient let LIQ_BACKOFF_MAX_SHIFT : Nat = 6; // cap at 30s << 6 = 32 min between retries + + // How long to wait before the next dispatch, given the current failure + // streak. Normal cadence until the streak clears the threshold, then double + // per additional failure up to the cap. + func liqDispatchIntervalNs() : Int { + if (_liqFailStreak < LIQ_FAIL_BREAK_THRESHOLD) { return HB_LIQ_NS }; + let shift = Nat.min(_liqFailStreak - LIQ_FAIL_BREAK_THRESHOLD + 1, LIQ_BACKOFF_MAX_SHIFT); + HB_LIQ_NS * (2 ** shift); + }; transient var _lastPriceNs : Int = 0; transient var _lastHeartbeatNs : Int = 0; // liveness beacon — stamped every heartbeat, even when paused transient var _lastFreezeNs : Int = 0; @@ -5922,6 +6215,18 @@ persistent actor Uplands { transient let HB_FINALISE_NS : Int = 500_000_000; // 0.5s — pending/GEPTOR/deferred transient let HB_AMM_NS : Int = 2_000_000_000; // 2s — requote (drift/cooldown gated inside) transient let HB_LIQ_NS : Int = 30_000_000_000; // 30s — liquidation batch + // Max loaned users examined per liquidation pass. runLiquidationBatch used to + // walk the WHOLE loans set in three phases with no cap, no cursor and no + // budget — the only unbounded sweep left on a fund-safety path. Past roughly + // 35k healthy loans that single message exceeds the 40B instruction limit and + // traps; underwater loans cost ~3.9x a healthy one, so the threshold collapses + // to ~10k when a cohort goes underwater together — i.e. the engine self- + // disabled exactly during the volatility event it exists to handle. The + // cursor below carries across beats the way tickTier/tickLeaderboardShard + // already do. Netting (Phase 2) now nets within a pass rather than across the + // whole book; that costs a little netting efficiency and buys a liquidation + // engine that cannot stop running. + transient let LIQ_BATCH_MAX : Nat = 2_000; transient let HB_PRICE_NS : Int = 30_000_000_000; // 30s — oracle refresh (HTTPS outcalls) transient let HB_REAP_NS : Int = 10_000_000_000; // 10s — closed-order reaper sweep transient let HB_SHIP_NS : Int = 10_000_000_000; // 10s — history shipper (queue → archive sidecar) @@ -5948,6 +6253,25 @@ persistent actor Uplands { // until the first successful status read. var _freezingLimitCycles : Nat = 0; var _computeAllocation : Nat = 0; // % of a core reserved (0 = best-effort); cached from canister_status + // The canister's REAL wasm_memory_limit, read back from canister_status + // alongside the two above. getCanisterInfo reports this and Stats → Canister + // renders consumption against it. + // + // This used to be a WASM_MEMORY_LIMIT_BYTES constant kept in sync with the + // deployed setting by hand, because the IC gave a canister no way to read its + // own limit. It does now (DefiniteCanisterSettings.wasm_memory_limit — the + // hand-declared ic00 reply record below includes it), and the hand-sync had + // already failed once in the dangerous direction: the subnet backend was + // created 2026-07-11 on the IC's 3 GiB default a month after ops raised the + // constant to 5.25 GiB, so it sat at 79% of its real ceiling while the + // dashboard reported a comfortable 45%. Reading it back cannot drift from + // the thing it describes. + // + // Stable, so it survives upgrades; 0 only on a brand-new canister before the + // first freeze-check tick (60s, HB_FREEZE_NS). Both frontend readers already + // guard `limit > 0` and degrade to omitting the ceiling rather than dividing + // by zero, so that window renders honestly instead of wrongly. + var _wasmMemoryLimit : Nat = 0; // Burn-rate telemetry, so the dashboard can show how fast fuel drains and // estimate time-to-freeze. _burnPerDay is the MEASURED total burn (storage + @@ -6137,6 +6461,29 @@ persistent actor Uplands { }; }; + // The actor had NO postupgrade hook at all, which is why the entropy latch + // below could survive an upgrade in a state that stopped it ever re-seeding: + // `_nameEntropySeeded` is stable and is assigned BEFORE its `await`, so an + // upgrade landing in that window kept the latch `true` while the pool it was + // guarding was never filled. Clearing it here makes the next heartbeat re-seed + // exactly once, which is the same remedy src/bridge/main.mo applies to its own + // set-before-await latches — and it costs one boolean rather than an `await` + // on every beat. + system func postupgrade() { + _nameEntropySeeded := false; + // Re-latch the genesis window from surviving state: an install upgraded + // across the introduction of `_ammEverEnabled` starts it false even on a + // long-live venue (a NEW stable var takes its declaration initializer). + // Any enabled pool proves the venue went live, so close the window before + // anything can observe it open. (A venue upgraded mid-season-reset has no + // pools to prove it — the next enableAmm latches then.) + if (not _ammEverEnabled) { + for ((_, p) in Map.entries(pools)) { + if (p.enabled) { _ammEverEnabled := true }; + }; + }; + }; + system func heartbeat() : async () { let now = Time.now(); // Stamp liveness *before* the pause gate: the IC genuinely fired the @@ -6149,6 +6496,17 @@ persistent actor Uplands { // Before this lands, drawUsername still varies (it folds in the clock) — // this replaces "varying" with "unpredictable". Ahead of the pause gate // so a paused venue still seeds. + // The latch is stable and set BEFORE the await, so an upgrade landing + // between the call and the reply destroyed the continuation while the latch + // survived as `true` — and nothing re-seeded, leaving the draw sequence a + // deterministic function of public registration timestamps, which is exactly + // what usernameFromDraws exists to prevent. The fix is the `postupgrade` + // below (the shape src/bridge/main.mo already documents and fixes), NOT a + // `_nameEntropy == 0` test here: that condition re-enters this branch on + // EVERY beat whenever the beacon is unavailable, and the `await` splits the + // heartbeat message, so the rest of the beat — requotes, heatmaps, shipping + // — stops running. Measured: it silently killed tickHeatmaps on a local + // replica where Random.blob() does not resolve. if (not _nameEntropySeeded) { _nameEntropySeeded := true; // set FIRST: a failed await must not retry every beat try { @@ -6164,7 +6522,10 @@ persistent actor Uplands { if (bal > _burnWinMax) { _burnWinMax := bal }; if (now - _lastFinaliseNs >= HB_FINALISE_NS) { _lastFinaliseNs := now; ignore finaliseExpiredPending() }; if (now - _lastAmmNs >= HB_AMM_NS) { _lastAmmNs := now; ignore tickAmm() }; - if (now - _lastLiqNs >= HB_LIQ_NS) { _lastLiqNs := now; ignore tickLiquidations() }; + // NOTE: _lastLiqNs is stamped by tickLiquidations ON COMPLETION, not here — + // a stamp assigned in this message would commit even when the batch traps. + // The CADENCE, though, is measured from dispatch: see _liqFailStreak. + if (armLiquidationDispatch(now)) { ignore tickLiquidations() }; if (now - _lastPriceNs >= HB_PRICE_NS) { _lastPriceNs := now; ignore tickPriceRefresh() }; if (now - _lastReapNs >= HB_REAP_NS) { _lastReapNs := now; reapClosedOrders() }; drainLedgerJournal(); // every beat, ahead of the shipper — ledger rows chase their semantic events @@ -6229,6 +6590,7 @@ persistent actor Uplands { _idleBurnPerDay := st.idle_cycles_burned_per_day; _freezingLimitCycles := st.idle_cycles_burned_per_day * st.settings.freezing_threshold / 86_400; _computeAllocation := st.settings.compute_allocation; + _wasmMemoryLimit := st.settings.wasm_memory_limit; } catch (_) {}; }; @@ -6884,7 +7246,7 @@ persistent actor Uplands { canister_status : shared { canister_id : Principal } -> async { cycles : Nat; idle_cycles_burned_per_day : Nat; - settings : { freezing_threshold : Nat; compute_allocation : Nat }; + settings : { freezing_threshold : Nat; compute_allocation : Nat; wasm_memory_limit : Nat }; }; // Attaches cycles from OUR balance to the target (the archive top-up path). deposit_cycles : shared { canister_id : Principal } -> async (); @@ -7249,7 +7611,19 @@ persistent actor Uplands { // the cap, seal the wedged archive at its acked prefix + drop the oldest // events (recording a gap). Doing this before L1 means L1 then re-anchors // onto ONE fresh archive rather than spawning one only to abandon it. - if (List.size(userEvents) >= shipHardCap()) { shedOldestEvents() }; + // Gate on BOTH conditions, as this block's own comment always said: + // "if shipping is broken AND the queue hit the cap". The code tested only + // the depth half, while the L1 roll on the very next line correctly + // consults _shipFailStreak. So a perfectly HEALTHY but backlogged shipper + // destroyed 50,000 events of permanent history with no adversary and no + // external cause: drain is capped at SHIP_BATCH_MAX per HB_SHIP_NS + // (200/s), so ~21 minutes at 400 events/s — or any stop/upgrade window + // that long, since heartbeats do not run while stopped — reached the cap + // on its own. Shedding is the response to a BROKEN shipper; a slow one + // should be allowed to catch up. + if (List.size(userEvents) >= shipHardCap() and _shipFailStreak >= shipRollThreshold()) { + shedOldestEvents(); + }; // ── L1: roll away from a persistently-failing archive — seal it at its // acked seq and install a fresh successor (a no-op seal after an L2 shed, // which already nulled the pointer; then it just installs the successor). @@ -7673,6 +8047,10 @@ persistent actor Uplands { // cycles" when the order-map leak bricked updates at the 3 GiB default // limit on 2026-06-10. ordersRetained is the leak telltale: orders are // currently never deleted, so this counts every order ever placed. + // The release this backend was built from — the trivially-queryable form + // of getCanisterInfo.appVersion (bots, scripts, curl). + public query func getAppVersion() : async Text { APP_VERSION }; + public query func getCanisterInfo() : async { canisterId : Text; cycles : Nat; @@ -7712,8 +8090,10 @@ persistent actor Uplands { treasuryIcpE8s : Nat; // …and ICP already swapped, ready to burn // (token, bps, atNs) — live primary-vs-XRC divergence alarms (the oracle banner) oracleDivergence : [(Text, Nat, Int)]; + appVersion : Text; // the release this wasm was built from (APP_VERSION) } { { + appVersion = APP_VERSION; canisterId = Principal.toText(Principal.fromActor(Uplands)); cycles = Cycles.balance(); freezingLimitCycles = _freezingLimitCycles; @@ -7725,7 +8105,7 @@ persistent actor Uplands { timersPaused = _timersPaused; memorySizeBytes = Prim.rts_memory_size(); heapLiveBytes = Prim.rts_heap_size(); - wasmMemoryLimitBytes = WASM_MEMORY_LIMIT_BYTES; + wasmMemoryLimitBytes = _wasmMemoryLimit; lowMemoryAtNs = _lowMemoryAtNs; ordersRetained = Map.size(orderStore.orders); tradesRetained = List.size(orderStore.trades); @@ -8005,6 +8385,7 @@ persistent actor Uplands { ignore Map.delete(deferredFok, Nat.compare, id); ignore Map.delete(deferredPostOnly, Nat.compare, id); ignore Map.delete(deferredExpiry, Nat.compare, id); + clearMmShield(d.owner, d.marketId); changed := true; }; case null {}; @@ -8056,6 +8437,20 @@ persistent actor Uplands { // to do, so callers (post-fill hook, timer scan) don't need a // pre-check. func tryLiquidate(user : Principal, now : Int) : Types.LiquidationOutcome { + // F1 lives HERE, not at the call sites. It used to be enforced only by the + // two batch sites, and the post-fill hook (adjustAffectedUsers) called + // straight through without it — so a fill could force a seize at a mark of + // any age while the batch sweeps correctly skipped the same user. The + // breaker manufactures exactly that window: a pended >2.5% jump + // deliberately does NOT advance refPriceUpdatedNs, so the frozen mark can + // sit far from the true price while fills keep arriving. Seizing there + // either takes a healthy account or lets an underwater one dodge, and the + // difference is socialized to LPs. marginPriceLookup returns a frozen + // refPrice of ANY age by design, so nothing downstream re-checks this. + // A guard inside the callee cannot be forgotten by a future call site. + // #err (not #healthy) because a stale mark means we could not ASSESS the + // user, which is a different claim from "this user is fine". + if (not userMarksFreshAt(user, now)) { return #err("stale mark — liquidation deferred") }; // H2: before seizing, if the user is genuinely liquidatable, free any // collateral parked in staged/resting orders back to spendable balance so // the seize reaches it (otherwise the liquidator can declare #insolvent and @@ -8380,6 +8775,31 @@ persistent actor Uplands { case (#sell) { (qp : Int) - haircut }; }; if (deltaPerUnit >= 0) { return #full }; // risk-reducing/improving fill + // DE-LEVER ESCAPE. The scoring above is purely the instantaneous + // LTV-weighted COLLATERAL delta; it never sees the debt the fill retires + // (deleveragePool runs after settlement). Closing a short means buying back + // the borrowed base: the pool spends quote at LTV 1.0 for base at LTV + // 0.9/0.85/0.8, so deltaPerUnit = ltv*mark - price is negative at every + // realistic close price. And `headroom` below is independent of trade size, + // so it is <= 0 exactly when health <= 1.25 — meaning #partial is + // unreachable and EVERY close price gets killed once health enters the + // 1.15-1.25 band. The user was then held until the liquidator took a 5% + // penalty a permitted close would have avoided. Two other sites promise the + // opposite ("you must always be able to de-lever"; "closing is always + // allowed"), and gateInitialMargin has the `newHealth < healthRatio` escape + // that this clamp lacked. A fill whose sign OPPOSES the pool's net position + // in that base is a genuine de-lever: let it through. + switch (Map.get(poolByPrincipal, Text.compare, Principal.toText(user))) { + case (?poolId) { + let net = poolNetSize(poolId, baseToken); + let reducesExposure = switch (side) { + case (#buy) { net < 0 }; // short (owes base) buying back + case (#sell) { net > 0 }; // long selling down + }; + if (reducesExposure) { return #full }; + }; + case null { }; + }; let now = Time.now(); BorrowEngine.accrueAll(loans, user, now); let h = BorrowEngine.getHealth(loans, marginAccounts, accounts, reservedBalance, user, marginPriceLookup); @@ -8721,6 +9141,7 @@ persistent actor Uplands { ignore Map.delete(deferredFok, Nat.compare, orderId); ignore Map.delete(deferredPostOnly, Nat.compare, orderId); ignore Map.delete(deferredExpiry, Nat.compare, orderId); + clearMmShield(d.owner, d.marketId); return #ok({ marketId = d.marketId; side = d.side }); }; case null {}; @@ -9044,7 +9465,8 @@ persistent actor Uplands { # "placeLimitOrdersBulk([...]) stages up to the per-owner cap in one batch. replaceMyOrder(cancelId, price, quantity) is an atomic cancel+place. " # "cancelMyOrder(id); cancelAllMyOrders(?marketId) with null = all markets. " # "COMMITMENT: a staged non-post-only order cannot be cancelled for its first 3s (anti-free-look) — the cancel errors with 'committed'; retry after release, or quote with post-only for instant cancel/replace. " - # "Guards: price must be within getMarketSpecs.priceBandFactor of the market ref price; order value must exceed minOrderNotionalUsd (spend-all dust exempt); a self-trade cancels YOUR resting maker. " + # "Guards: price must be within getMarketSpecs.priceBandFactor of the market ref price; order value must exceed minOrderNotionalUsd (spend-all dust exempt); a self-trade cancels YOUR resting maker; " + # "one price level holds at most " # Nat.toText(Types.MAX_ORDERS_PER_PRICE_LEVEL) # " resting orders per side (placement at a full level is rejected — quote one tick away). " # "PRICE CONVERGENCE: a protocol arbitrageur (public: getArbStats) imports/exports synthetic supply at the oracle mark and takes any order resting >~0.5% off the mark — quoting far off-mark is donating to it.\n\n" # "## Safety: arm the dead-man switch\n" # "cancelAllAfter(?expiresInSec): if you call no trading method within the window, ALL your resting orders auto-cancel. Re-armed by every trading call; null disarms. A crashed quoting bot's orders won't hang. Bounds are in getMarketSpecs.\n\n" @@ -9076,7 +9498,7 @@ persistent actor Uplands { # "30s snapshots (the time axis of the heat surface), oldest first, incremental by computedNs cursor. getMarginRiskSummary(): the vault's loan book in aggregate " # "(debt, utilisation vs cap, insurance buffer, liquidatable exposure) — identifies nobody.\n\n" # "## Reading data\n" - # "schema() describes the queryable entities; execute(queryJson) runs a query over markets/orders/your own pools+positions+balances; archiveExecute(...) reaches deep history. Other users' private rows are filtered out server-side.\n\n" + # "schema() describes the queryable entities; execute(queryJson) runs a query over markets/orders/your own pools+positions+balances; archiveExecute(...) reaches deep history. Owner-scoped entities (pool/position/balance/closedOrder) return only YOUR rows. NOTE: the deposit/withdrawal ledger is deliberately PUBLIC and principal-attributed — archiveExecute returns every user's D/W rows, by design (anti-mixer transparency), not just your own.\n\n" # "## Identity\n" # "Bots authenticate with a raw Ed25519 keypair (Internet Identity is browser-only). Your funded principal is the one that deposited. " # "ALL update calls refuse anonymous callers at the gate — sign every mutation; queries work anonymously.\n\n" @@ -9244,6 +9666,17 @@ persistent actor Uplands { // Cap pools per owner — unbounded creation is a DoS amplifier (O(pools) // scans + a `loans` entry per borrowing pool that the liquidation heartbeat // walks). O(1) check against the per-owner counter. + // The count cap bounds POOLS; this bounds PAYLOAD. `name` was stored raw + // with no length check, so 64 pools/identity x unlimited identities x a + // multi-KB name reached the memory wall from a different door than + // setUserPreferences, with no funding, registration or minimum balance + // required to get there. It is also the only user-authored free-text field + // anywhere in the OQL surface, so it is what an indirect prompt injection + // would ride into a controller's assistant session — another reason to keep + // it short and boring. + if (name.size() > MAX_POOL_NAME_LEN) { + return #err("Pool name too long (max " # Nat.toText(MAX_POOL_NAME_LEN) # " characters)."); + }; let ownerKey = Principal.toText(msg.caller); let owned = Option.get(Map.get(ownerPoolCount, Text.compare, ownerKey), 0); if (owned >= MAX_POOLS_PER_OWNER) { @@ -9422,12 +9855,32 @@ persistent actor Uplands { }; case (?_) {}; }; - // Upsert the position record (provisional entry; size is derived at read). + // Upsert the position record (provisional entry; size is derived at read + // — but see below, the STORED size must survive this upsert). + // + // This used to write `size = 0` on every open, on the theory that reads + // derive size anyway. Reads do — but settlement does NOT: bookPoolSide + // bases MarginPools.applyFill on the STORED size, and applyFill's whole + // VWAP/realize branch turns on it. Zeroing it opened a race: if the new + // order was IMMEDIATELY marketable (a resting counter-order at its + // price), its fill settled on the next GEPTOR release before anything + // else touched the pool, applyFill saw size == 0, and booked a REDUCING + // fill as a fresh OPEN — entry dragged to the fill price, realizedDelta + // zero, and every later reduce realizing against the corrupted entry + // (observed live 2026-08-01: a 39,960-ICP long entered at 2.07 and + // unwound at 2.0725 froze Realized at $50 and showed entry 2.0725). + // Orders that RESTED a while escaped only by luck: tryLiquidate's + // unconditional reconcilePoolPositions (post-fill hook + timer scans) + // usually restored the stored size to the derived truth first — same + // wipe, different winner of the race. Increases raced identically (a + // marketable add-on re-based entry to its own fill price instead of the + // blended VWAP). Preserving the prior size closes the race for both; + // a genuinely new position still starts at 0. let k = posKey(poolId, marketId); let prior = Map.get(poolPositions, Text.compare, k); Map.add(poolPositions, Text.compare, k, { poolId; marketId; baseToken; - size = 0; + size = switch (prior) { case (?p) { p.size }; case null { 0 } }; entryPrice = switch (prior) { case (?p) { if (p.entryPrice > 0) { p.entryPrice } else { refPrice } }; case null { refPrice } }; realizedPnl = switch (prior) { case (?p) { p.realizedPnl }; case null { 0 } }; openedAt = switch (prior) { case (?p) { p.openedAt }; case null { now } }; @@ -10578,7 +11031,16 @@ persistent actor Uplands { case (?e) { return #err(e) }; case null {}; }; - let valueBefore = insurancePoolValue(); + // MINT prices against cash PLUS the fund's receivable. insuranceOwedUsd is + // penalties already EARNED by existing stakers that the vault has not yet + // moved across ("a liability of the vault and a claim of the fund"). Pricing + // the mint on cash alone sold shares below their economic value: the + // newcomer then took a pro-rata slice of a payment they did not earn as soon + // as settleInsuranceArrears ran. insuranceShareValue() deliberately stays + // cash-only for REDEMPTION so an unstake is always payable — the two figures + // are supposed to differ, and this is the one input the virtual offsets + // below do not cover. + let valueBefore = insurancePoolValue() + insuranceOwedUsd; // FIRST-STAKER FLOOR. With an empty pool one base unit buys one share, and // that share then owns everything the pool subsequently receives — // liquidation penalties flow in WITHOUT minting, so value climbs while @@ -10604,6 +11066,11 @@ persistent actor Uplands { // shares AND emits an #insShareDelta per holder so the ledger replay stays // reconcilable — a deliberate governance action, not a side effect of the // next staker walking in. + // NOTE: valueBefore is cash + insuranceOwedUsd, so this now fires only when + // BOTH are zero — which is exactly when the claim "existing shares are worth + // nothing" is actually true. A pool drained to zero cash but still owed an + // unpaid penalty has shares worth that receivable, and a stake against it is + // fairly priced; refusing there would have been the wrong call. if (insuranceShareSupply > 0 and valueBefore == 0) { return #err("The insurance pool has been fully absorbed by bad debt, so existing shares are worth nothing. Staking now would hand most of your stake to those wiped-out holders. The tranche must be formally restarted before it can take new stakes."); }; @@ -10665,7 +11132,31 @@ persistent actor Uplands { return #err("Repay your outstanding loan before unstaking from the insurance pool"); }; if (insuranceShareSupply == 0) { return #err("Pool is empty") }; - let payout = Fixed.mulDiv(insurancePoolValue(), shares, insuranceShareSupply, false); + // SYMMETRIC with the mint. The virtual offset was added to stakeInsurance + // (commit e66a27e, "closing the parity gap with the LP vault") but never to + // this side, and unstakeInsurance had not been touched since the initial + // commit. An offset applied to ONE direction is not a safety device, it is + // an arbitrage: mint uses (S+v)/(V+v) while redeem used the raw S/V, so + // redeem(mint(A)) > A whenever share value exceeds 1.0 — which is the + // NORMAL state, because liquidation penalties accrue to the fund without + // minting shares. A staker could enter just before a penalty landed and + // leave just after, skimming from the long-term stakers who carry the + // bad-debt risk, with no exit fee and no minimum duration to stop them. + // Measured at +104,165 base units on a $10k stake at share value 1.0333. + // The LP tranche has the same shape but LP_EXIT_FEE_BPS = 40 swamps the + // sub-basis-point edge; insurance has no such fee, so it needs the symmetry. + // Offsetting BOTH directions makes the round trip the identity again. + let rawPayout = Fixed.mulDiv( + insurancePoolValue() + INSURANCE_VIRTUAL_VALUE, + shares, + insuranceShareSupply + INSURANCE_VIRTUAL_SHARES, + false, + ); + // Clamp to what the pool actually holds. The symmetric form can exceed cash + // when share value is BELOW 1.0 (i.e. after absorbBadDebt has eaten into the + // tranche): there S > V, and S*(V+v)/(S+v) > V. Redemption must never write + // a cheque the buffer cannot cover. + let payout = Nat.min(rawPayout, insurancePoolValue()); if (not subInsuranceShares(msg.caller, shares)) { return #err("Share subtraction failed") }; insuranceShareSupply -= shares; if (not Accounts.subtractBalance(accounts, insurancePrincipal(), Types.QUOTE_TOKEN, payout)) { @@ -10923,7 +11414,7 @@ persistent actor Uplands { let w = walkFillable(marketId, baseToken, side, cap, budget); let impact : Nat = if (w.base > 0 and ref > 0) { let avg = Fixed.div(w.quote, w.base, true); - let dev = if (avg > ref) { avg - ref } else { ref - avg }; + let dev = SafeMath.subOrZero(avg, ref) + SafeMath.subOrZero(ref, avg); // |avg − ref| Fixed.div(dev, ref, true) / 10_000; // e8 fraction → bps } else { 0 }; ?{ base = w.base; quote = w.quote; fee = quoteFeeFor(msg.caller, w.quote, #takerDebit); impactBps = impact }; @@ -11441,6 +11932,7 @@ persistent actor Uplands { ignore Map.delete(deferredFok, Nat.compare, orderId); ignore Map.delete(deferredPostOnly, Nat.compare, orderId); ignore Map.delete(deferredExpiry, Nat.compare, orderId); + clearMmShield(d.owner, d.marketId); bumpUserVersion(msg.caller); return #ok; }; @@ -11625,6 +12117,19 @@ persistent actor Uplands { case null { return #err("Market not found: " # marketId) }; case (?(base, _)) { base }; }; + // REFUSE to overwrite a live pool. This used to putPool an emptyPool + // unconditionally, and emptyPool has refPrice = 0 — so one call against a + // market that already holds capital zeroed that leg's NAV contribution + // without pausing mints. That needs no adversary: it is the shape of an + // honest "reconfigure this pool" mistake. Use setAmmConfig to retune an + // existing pool; deliberate re-seeding goes through an explicit reset. + switch (AMM.getPool(pools, marketId)) { + case (?_) { + return #err("An AMM pool already exists for " # marketId + # ". Overwriting it would reset refPrice to 0 and mark that leg of the vault at zero — use setAmmConfig to retune it."); + }; + case null { }; + }; let fresh = AMM.emptyPool(marketId, baseToken); AMM.putPool(pools, fresh); #ok; @@ -11826,11 +12331,22 @@ persistent actor Uplands { case (?p) { let u = AMM.withEnabled(p, enabled); AMM.putPool(pools, u); + // First enable = the venue goes live: the genesis window for + // injectHistoricalTrades closes here, one-way (see the latch decl). + if (enabled) { _ammEverEnabled := true }; #ok; }; }; }; + // The genesis latch, readable: "has any market ever been enabled on this + // install". No posture logic here — it reports the latch, and callers + // combine it with getDeployMode (on #play, latch unset ⟺ the backdrop + // window is still open). Public on purpose: whether a venue ever went + // live is already public knowledge from the tape; ops scripts and the + // posture tests read this to tell pre- from post-genesis refusals apart. + public query func getAmmEverEnabled() : async Bool { _ammEverEnabled }; + // Admin-set reference price. In production this is supplied by the // HTTPS-outcall oracle; for local dev we let an admin poke it so the // AMM can be tested without network access. @@ -12143,6 +12659,8 @@ persistent actor Uplands { let aiRefusalLog = Map.empty(); // refusal ts (ns), pruned to the ban window let aiBanUntil = Map.empty(); // principal → suspension end (ns) transient let AI_REFUSALS_BAN_N : Nat = 3; // refusals inside 24h that trip a suspension + // Max caller-supplied prompt bytes — see aiComplete. ~4x a real frontend turn. + transient let AI_MAX_PROMPT_BYTES : Nat = 32_768; transient let AI_BAN_NS : Int = 86_400_000_000_000; // 24h func aiBump(caller : Principal, f : AiUsage -> AiUsage) { @@ -12221,10 +12739,19 @@ persistent actor Uplands { case null {}; }; }; - // The guard preamble goes AHEAD of the caller-supplied prompt so its - // rules outrank anything a raw caller (or a jailbreak inside the app's - // transcript) can inject. - let guarded = AI_GUARD_PREAMBLE # prompt; + // BOUND THE PAYLOAD. The rate limiter caps call COUNT, not COST, while + // outcall pricing is dominated by request bytes — so an unbounded prompt let + // one caller multiply the per-call cycle burn roughly 8x over the ~8-15 KB a + // real frontend turn sends, and the canister pays. 32 KiB is four times the + // legitimate ceiling. + if (Text.size(prompt) > AI_MAX_PROMPT_BYTES) { + return #err("Prompt too long (max " # Nat.toText(AI_MAX_PROMPT_BYTES) # " bytes)."); + }; + // The guard preamble rides the provider's SYSTEM channel (below), not the + // user turn. Concatenating it into the same `user` message gave it no more + // standing than the attacker text that followed it — the weakest possible + // placement for rules that are supposed to outrank the prompt. + let guarded = prompt; // Per-provider request shape; bodies are built with mo:json so the prompt // is escaped. Everything downstream of the tuple (outcall, status check, // extract-the-text) is provider-agnostic: textPath is the JSON path to the @@ -12235,7 +12762,20 @@ persistent actor Uplands { ("contents", #array([ #object_([ ("parts", #array([ #object_([ ("text", #string(guarded)) ]) ])), ]) ])), - ("generationConfig", #object_([ ("temperature", #number(#float(0.0))) ])), + // Platform rules in the dedicated system channel, where they are not + // just more user text the model can be argued out of. + ("systemInstruction", #object_([ + ("parts", #array([ #object_([ ("text", #string(AI_GUARD_PREAMBLE)) ]) ])), + ])), + // maxOutputTokens matters for COST, not just tidiness: the Anthropic + // branch bounds output with max_tokens, but Gemini set only + // temperature — so a caller could steer a completion past + // max_response_bytes (100 KB) and have the outcall REJECTED after the + // full charge was already paid. A deterministic waste primitive. + ("generationConfig", #object_([ + ("temperature", #number(#float(0.0))), + ("maxOutputTokens", #number(#int(4096))), + ])), ]); ( "https://generativelanguage.googleapis.com/v1beta/models/" @@ -12259,6 +12799,10 @@ persistent actor Uplands { ("max_tokens", #number(#int(4096))), ("thinking", #object_([ ("type", #string("disabled")) ])), ("output_config", #object_([ ("effort", #string("low")) ])), + // Top-level `system` — the Messages API's dedicated channel for + // platform rules. Previously the preamble was concatenated into the + // user turn, which carries no more priority than the attacker text. + ("system", #string(AI_GUARD_PREAMBLE)), ("messages", #array([ #object_([ ("role", #string("user")), ("content", #string(guarded)), @@ -12312,6 +12856,15 @@ persistent actor Uplands { // Stats-grade self-report (bounded arg, counter-only — not enforcement). public shared (msg) func aiActionExecuted(method : Text) : async () { requireAuth(msg.caller); + // Same registration gate aiComplete applies. aiComplete's own comment says + // that gate exists to stop "unbounded growth of the never-evicted + // aiCallLog/aiUsage maps" — and this method writes aiUsage with only + // requireAuth, so it was a clean bypass of the stated invariant. The + // Text.size check below bounds an argument that is DISCARDED; the real map + // key is the caller principal, whose cardinality was unbounded. aiUsage is + // also untouched by performWorldWipe, so there is no operator remedy short + // of an upgrade. + if (Option.get(Map.get(registeredUsers, Text.compare, Principal.toText(msg.caller)), false) == false) { return }; if (Text.size(method) > 64) { return }; aiBump(msg.caller, func(u) { { u with actionsExecuted = u.actionsExecuted + 1 } }); }; @@ -12707,6 +13260,21 @@ persistent actor Uplands { // left a window to mint LP against marks the whole venue knew were stale.) transient let LP_DEPOSIT_MAX_REF_AGE_NS : Int = 60_000_000_000; + // Raw HELD quantity of a leg, independent of whether it can be priced. Guards + // that mean "does the vault hold this?" must use this, not the *ValueUsd + // sibling below — the product silently answers "no" for a held-but-unpriced + // leg, which is exactly the case a guard needs to catch. + func vaultAssetBalance(vv : VaultValue, token : Types.TokenId) : Nat { + switch (token) { + case ("BTC") { vv.basket.btc }; + case ("ETH") { vv.basket.eth }; + case ("SOL") { vv.basket.sol }; + case ("ICP") { vv.basket.icp }; + case ("ICPUSD") { vv.basket.icpusd }; + case (_) { 0 }; + }; + }; + func vaultAssetValueUsd(vv : VaultValue, token : Types.TokenId) : Nat { switch (token) { case ("BTC") { Fixed.mul(vv.basket.btc, vv.prices.btc, false) }; @@ -12762,7 +13330,32 @@ persistent actor Uplands { func vaultPricesStale(now : Int, vv : VaultValue) : ?Text { let legs = [("BTC", "BTC-ICPUSD"), ("ETH", "ETH-ICPUSD"), ("SOL", "SOL-ICPUSD"), ("ICP", "ICP-ICPUSD")]; for ((asset, market) in legs.vals()) { - if (vaultAssetValueUsd(vv, asset) > 0) { // vault holds it (and it's priced) + // Test the BALANCE, not the value. This used to read + // `vaultAssetValueUsd(vv, asset) > 0`, which is balance x price — so a leg + // the vault genuinely HOLDS but cannot PRICE (refPrice 0) multiplied out + // to 0 and was skipped entirely: no staleness check, no pending-jump + // check, while currentVaultValue simultaneously marked that whole leg at + // $0. The comment "vault holds it (and it's priced)" conflated two + // conditions into one product, and the failure of the second silently + // disabled the first. A deposit on a DIFFERENT leg then minted against an + // understated NAV and redeemed a pro-rata slice of the full basket, + // including the leg marked at zero. The totalQuoteValue == 0 backstop only + // fires if the WHOLE basket marks to zero, so it never caught this. + let held = vaultAssetBalance(vv, asset) > 0; + if (held) { + // A held leg we cannot price must BLOCK the mint, not be valued at zero. + switch (AMM.getPool(pools, market)) { + case (?p) { + if (p.refPrice == 0) { + return ?("LP deposit paused: the vault holds " # asset # " but has no reference price for it — minting now would value that leg at zero"); + }; + }; + case null { + return ?("LP deposit paused: the vault holds " # asset # " but has no AMM pool to price it"); + }; + }; + }; + if (held) { // M1 (security review): an ACTIVE pending jump means the market has // plausibly moved ≥2.5% but refPrice is deliberately frozen awaiting a // confirming reading — a mint now would mark the basket at the @@ -12896,9 +13489,24 @@ persistent actor Uplands { // ── Auto-refresh parameters ── // Minimum sources required to trust the aggregate; lower than - // PRICE_SOURCES.size() so a single provider outage doesn't freeze - // the AMM. - transient let PRICE_MIN_SOURCES : Nat = 2; + // PRICE_SOURCES.size() so a single provider outage doesn't freeze the AMM. + // + // THREE, not two. At n=2 the robust aggregation is not robust at all: + // PriceFeed.trimOutliers returns the sample set untrimmed below 3 readings, + // so the filter is inoperative at exactly the minimum we accept — and the + // median's breakdown point at n=2 is zero, so one rogue or glitching source + // carries 50% weight of a mark that drives collateral valuation and + // liquidations. (Both defences need the good readings to be a MAJORITY, which + // is what n=2 cannot supply: the trim locates the cluster with a median and a + // MAD, and the median's breakdown point is what bounds the result. That is + // why 3 is the floor that matters.) The 50 bps + // dispersion gate bounds how far a 2-source disagreement could push the mark, + // but "bounded damage with no filter" is not the guarantee this constant is + // supposed to provide. With 8 wired sources, requiring 3 still tolerates a + // five-provider outage before the mark freezes. + // Single source of truth: the floor and the reasoning behind it live in + // PriceFeed, next to the trim and median it is derived from. + transient let PRICE_MIN_SOURCES : Nat = PriceFeed.MIN_ROBUST_SOURCES; // = 3 // Test pin for the source floor (controller + non-production) so the // degraded/fallback path is deterministically testable — real source // outages can't be staged in an integration test. @@ -12920,6 +13528,8 @@ persistent actor Uplands { transient var _priceRefreshSuccess : Nat = 0; transient var _priceRefreshFailure : Nat = 0; transient var _priceRefreshInFlight : Bool = false; + // Per-market GEPTOR fetch in-flight set — see processGeptorDue. + transient let _geptorInFlight = Map.empty(); // Sudden-jump circuit breaker: count refreshes that were rejected // pending a confirming reading. See acceptOrPendPrice below. transient var _priceRefreshSuspended : Nat = 0; @@ -13202,6 +13812,21 @@ persistent actor Uplands { switch (AMM.getPool(pools, marketId)) { case null { #rejected("No AMM pool for " # marketId) }; case (?p) { + // MONOTONIC IN SAMPLE TIME. refPriceUpdatedNs used to be stamped from + // the CONTINUATION clock (`now`) with no comparison against the value + // already on the pool, so an aggregate whose readings were sampled long + // ago could overwrite a fresher mark AND be stamped brand new. That one + // timestamp gates ~30 references — AMM requote, panic-cancel, + // extMarketSwap's age check, the LP-mint staleness gate, order-release + // anti-snipe, margin freshness — so a false stamp satisfies every one of + // them at once, which is the exact opposite of the anti-snipe premise + // ("they were placed before this fetch, so they can't be sniping a stale + // price"). Refuse any aggregate sampled at or before the mark we already + // have. The XRC fallback below is unaffected: it carries its own anchor + // freshness. + if (agg.timestamp <= p.refPriceUpdatedNs) { + return #rejected("stale aggregate: sampled at or before the current mark"); + }; let primaryOk = agg.sourceCount >= minSources() and agg.price > 0.0 and agg.stddevBps <= PRICE_MAX_STDDEV_BPS; if (primaryOk) { let newPx = Fixed.fromFloat(agg.price); // PriceFeed Float → Nat boundary @@ -13427,8 +14052,14 @@ persistent actor Uplands { }; } catch (_) { _priceRefreshFailure += 1; + } finally { + // `finally`, not a bare statement after the try/catch. Motoko's `catch` + // only intercepts thrown Errors, not traps, and an early `return` skips a + // trailing statement entirely — either path used to leave this transient + // flag latched TRUE, which silently disables the periodic price refresh + // until the next upgrade. tickShipEvents already uses this idiom. + _priceRefreshInFlight := false; }; - _priceRefreshInFlight := false; }; // (tickPriceRefresh is now dispatched by the heartbeat, not a timer.) @@ -13701,7 +14332,19 @@ persistent actor Uplands { let ethHeld = Accounts.getBalance(accounts, amm, "ETH"); let solHeld = Accounts.getBalance(accounts, amm, "SOL"); let icpHeld = Accounts.getBalance(accounts, amm, "ICP"); - let icpusdHeld = Accounts.getBalance(accounts, amm, Types.QUOTE_TOKEN); + let icpusdHeldGross = Accounts.getBalance(accounts, amm, Types.QUOTE_TOKEN); + // THE ARREARS SLICE COMES OFF THE CASH LEG. currentVaultValue haircuts NAV + // by insuranceOwedUsd (penalties the vault owes the insurance fund), so the + // MINT prices against the reduced figure — but redemption read gross + // holdings and ignored that liability entirely. That FLIPS the sign of the + // loan-book asymmetry documented above: the loan book makes redemption <= + // fair (safe), whereas ignoring a liability the mint counted makes it > + // fair. A deposit-then-withdraw round trip while arrears were outstanding + // came out net-positive, funded by the LPs who stayed. Deducting the + // exiting share's slice of the arrears — rounded UP, against the exiter — + // restores the safe direction and mirrors how the loan book is handled. + let arrearsSlice = Fixed.mulDiv(insuranceOwedUsd, lpAmount, vaultLPSupply, true); + let icpusdHeld : Nat = SafeMath.subOrZero(icpusdHeldGross, arrearsSlice); let keepBps : Nat = 10_000 - LP_EXIT_FEE_BPS; func netLeg(held : Nat) : Nat { Fixed.mulDiv(Fixed.mulDiv(held, lpAmount, vaultLPSupply, false), keepBps, 10_000, false); @@ -14070,6 +14713,11 @@ persistent actor Uplands { // retried Phase-N admission apply twice in Phase N+1. principalEmail // stays: identity, not season state — nobody re-verifies with Google. Map.clear(playDepositUsedUsd); + Map.clear(playDepositUsedByEmail); // the bucket every BOUND player actually uses — + // on #play, funding requires a Google binding, so + // missing this map re-arms nobody real. Found live + // at the Phase I→II reset (local tests have no + // bindings, so the principal bucket masked it). Map.clear(playReservedUnits); // Per-user hot history views: a $0 wallet above last season's deposit // rows reads as a bug. The durable copies live in the sealed season @@ -14275,6 +14923,28 @@ persistent actor Uplands { #ok(rec); }; + // Season-boundary repair: re-arm the play allowance for EVERYONE, without + // touching anything else. Exists because the Phase I→II resetSeason cleared + // only the principal-keyed bucket; every bound player consumes from the + // email-keyed one, so their Phase II allowance stayed spent until this ran. + // Kept as an ops tool: it is the allowance leg of resetSeason, alone. + // Same posture gate as the other balance-adjacent admin ops. + public shared (msg) func adminResetPlayAllowances() : async { #ok : Text; #err : Text } { + if (IS_PRODUCTION) { return #err("no play allowances exist on #production") }; + requireController(msg.caller); + let nP = Map.size(playDepositUsedUsd); + let nE = Map.size(playDepositUsedByEmail); + let nR = Map.size(playReservedUnits); + Map.clear(playDepositUsedUsd); + Map.clear(playDepositUsedByEmail); + Map.clear(playReservedUnits); + logEvent("info", "system", "Play allowances re-armed: cleared " + # Nat.toText(nP) # " principal bucket(s), " # Nat.toText(nE) + # " email bucket(s), " # Nat.toText(nR) # " reservation(s)", null); + #ok("cleared " # Nat.toText(nP) # " principal + " # Nat.toText(nE) + # " email bucket(s), " # Nat.toText(nR) # " reservation(s)"); + }; + // Admin: purge 0-qty zombie orders (residue from a historical matching // engine bug — a taker whose entire quantity went into pending matches // used to leave a 0-qty #open record in the book indexes, which would @@ -14310,6 +14980,34 @@ persistent actor Uplands { records : [{ price : Nat; quantity : Nat; timestamp : Int }], ) : async { #ok : Nat; #err : Text } { requireController(msg.caller); + // Posture gate, GENESIS-WINDOW variant (decision 2026-08-06). History: + // this had NO posture gate at all (OhShii #10.6b), then the August audit + // made it #dev-only — which broke the #play bring-up's chart backdrop + // (play_start.sh / deploy.sh inject BEFORE the first enableAmm). It + // cannot move refPrice (marketStats feeds display and analytics only — + // lastPrice, candles, 24h volume, the OQL market row), so it is chart + // forgery rather than value manipulation — and the #play invariant is + // that the operator does not move prices, forged or otherwise, once the + // venue is LIVE. Hence a one-way window: + // #dev — unrestricted, like every dev hook. + // #play — accepted only until the first enableAmm of this + // install (_ammEverEnabled — survives upgrades AND + // season resets; only a reinstall re-arms it). + // #production — never: a real-money venue gets no synthetic tape. + // Refusal is a typed #err, not a trap: THE RULE at requireDevHook is to + // refuse loudly in whichever way the signature allows, and this method + // has a Result channel. + switch (DEPLOY_MODE) { + case (#dev) {}; + case (#play) { + if (_ammEverEnabled) { + return #err("injectHistoricalTrades: genesis window closed — a market has been enabled on this #play install, and from that point the operator does not move prices, forged or otherwise. Only a reinstall (a new venue) re-arms the window."); + }; + }; + case (#production) { + return #err("injectHistoricalTrades is not available on #production"); + }; + }; ensureInit(); let _ = switch (Map.get(markets, Text.compare, marketId)) { case null { return #err("Market not found: " # marketId) }; @@ -14864,6 +15562,17 @@ persistent actor Uplands { // entity); position/pool/balance stay #controllerOrScoped. OQL.Entity.manual("order", func () = orderStore.orders.values().filter(func o = OrderBook.isOpen(o)), "Order", "id") .sample({ id = 0; marketId = ""; owner = Principal.fromText("aaaaa-aa"); side = #buy; orderType = #limit; price = 0; quantity = 0; filled = 0; status = #open; timestamp = 0; originalQuantity = 0 }) + // `id` MUST stay projected — it is this entity's declared primary key + // and the OQL executor traps on a hidden pk. It is also half of a + // de-anonymisation join: the moment a resting order takes its first + // partial fill, an ATTRIBUTED #fill{ orderId } event lands on the public + // tape, so order.id == fill.orderId recovers the owner of a + // partially-filled order WHILE IT IS STILL RESTING — exactly what + // "PUBLIC but UNATTRIBUTED" is supposed to prevent. The OQL half of that + // join is already shut — the public `userEvent` projection does not + // carry orderId — but the archive's RAW getEventsRange still returns the + // whole #fill variant, so the join survives there until that surface + // redacts per-kind. Tracked in docs/issue-triage-2026-08.md §3. .payload("id", func o = o.id) .payload("marketId", func o = o.marketId) .edge("marketId", "market") diff --git a/src/backend/mixins/UserAccount.mo b/src/backend/mixins/UserAccount.mo index 6dae2b5..4a77e11 100644 --- a/src/backend/mixins/UserAccount.mo +++ b/src/backend/mixins/UserAccount.mo @@ -55,6 +55,12 @@ mixin ( recordDeposit : (Principal, Types.DepositRecord) -> (), ) { + // Bounds on the one payload a free, unregistered identity can persist. + // 8 is generous against a frontend that slices to 3; a market id like + // "BTC-ICPUSD" is 10 chars, so 32 leaves room without admitting a blob. + transient let PREFS_MAX_RECENT_MARKETS : Nat = 8; + transient let PREFS_MAX_MARKET_LEN : Nat = 32; + // ── Profile ───────────────────────────────────────────────── // Update call (may create profile on first hit). Auto-generates a // friendly username; the user can regenerate up to MAX_USERNAME_REGENS @@ -106,8 +112,24 @@ mixin ( }; }; + // VALIDATED BEFORE IT PERSISTS. requireAuth rejects only the anonymous + // principal — it is not an authorization boundary — so this was the one write + // endpoint reachable before any anti-Sybil control, storing an unbounded, + // unvalidated blob per free identity with no eviction and no purge path + // (performWorldWipe clears ~60 maps and never touched this one). The only + // ceiling was the ~2 MiB ingress limit, so a few thousand calls from rotating + // throwaway identities reached the memory wall the order-map leak already hit + // once. The frontend's slice-to-3 is a client convention, not a control. public shared (msg) func setUserPreferences(prefs : Types.UserPreferences) : async () { requireAuth(msg.caller); + if (prefs.recentMarkets.size() > PREFS_MAX_RECENT_MARKETS) { return }; + for (m in prefs.recentMarkets.vals()) { + if (m.size() > PREFS_MAX_MARKET_LEN) { return }; + }; + switch (prefs.lastMarket) { + case (?m) { if (m.size() > PREFS_MAX_MARKET_LEN) { return } }; + case null { }; + }; let key = Principal.toText(msg.caller); Map.add(userPreferences, Text.compare, key, prefs); }; diff --git a/src/bridge/main.mo b/src/bridge/main.mo index 352aba3..b9bc446 100644 --- a/src/bridge/main.mo +++ b/src/bridge/main.mo @@ -35,10 +35,68 @@ import Blob "mo:core/Blob"; persistent actor Bridge { + // ── Deployment posture ─────────────────────────────────────────── + // MIRRORS the DEX's posture axis (src/backend/main.mo — same type, same + // committed default, same `transient` reasoning). The Bridge had NO posture + // concept at all: devSimulateDeposit/devConfirmDeposits were labelled + // "DEV ONLY" in a comment and gated by nothing, and their only real + // restraint was the DEX's playDepositCap() — which is active on #play but + // returns null on BOTH #dev and #production. So on a flip to #production + // these methods (and the claim they feed) would mint UNCAPPED balance into + // a value-bearing DEX, and icp.yaml wires this same stub into both + // production-facing targets (`engine` and `subnet`/multidex.ai). The gate + // now lives here rather than being outsourced. + // + // `transient` is load-bearing exactly as on the DEX: a plain `let` in a + // `persistent actor` is implicitly STABLE, so an edited literal would be + // silently overwritten by the stored value on `--mode upgrade` and the flip + // would never land. Transient re-evaluates on install AND upgrade. + // + // The DEX also derives an IS_DEV from this literal; the Bridge deliberately + // does not, because nothing here is #dev-only (see requireNotProduction + // below for why the line sits at #production). A declared-but-unread + // constant is a dead binding — moc says so itself, M0194 — and this file + // already carries one such fossil (ASSETS, declared and never used as a + // membership test until now). Add IS_DEV when something reads it. + public type DeployMode = { #dev; #play; #production }; + transient let DEPLOY_MODE : DeployMode = #play; + transient let IS_PRODUCTION : Bool = DEPLOY_MODE == #production; + + // THE RULE for every posture gate, copied from the DEX's requireDevHook: + // refuse LOUDLY, in whichever way the signature allows — a typed #err where + // there is a Result channel, a trap where there is not. Never a silent + // return. + // + // WHY THE LINE IS AT #production AND NOT AT #dev. The `dev` prefix on these + // two methods is a naming fossil: devSimulateDeposit is the LIVE #play + // on-ramp. It is what the Deposit page calls (src/frontend/src/main.js), it + // is what drives the play allowance (DEX playDepositReserve → + // playDepositCap → PLAY_DEPOSIT_CAP_USD, which is ONLY non-null on #play), + // and tests/test_play_deposit_cap.sh exercises it on that posture. Gating + // it to #dev would delete the only on-ramp the committed default posture + // has. The hole the gate must close is the UNCAPPED posture — #production, + // where playDepositCap() returns null and nothing else stands between an + // authenticated caller and freshly minted DEX balance. + func requireNotProduction(name : Text) : { #ok; #err : Text } { + if (IS_PRODUCTION) { + return #err(name # " is disabled on #production: this Bridge is a STUB with no custody behind it, so any balance it credits would be unbacked. Deploy the real chain-key Bridge (docs/bridge-and-cks-design.md) before flipping the posture."); + }; + #ok; + }; + // Depositable assets = the DEX's token ids. ICPUSD stands in for a USD // stablecoin (e.g. USDC). transient: a tunable constant, reset on upgrade. transient let ASSETS : [Text] = ["BTC", "ETH", "SOL", "ICP", "ICPUSD"]; + // ASSETS was declared and never used as a membership test, and there is no + // length check anywhere in this file — so an arbitrary Text went straight + // into a permanent map key on both write paths. Every entry point that can + // reach `ledgers` validates through here FIRST. + func isSupportedAsset(asset : Text) : Bool { + for (a in ASSETS.vals()) { if (a == asset) { return true } }; + false; + }; + // Per-(user, asset) deposit ledger — the CREDIT axis. `confirmed` is cumulative // confirmed inflow (monotonic); `pending` is simulated-but-unconfirmed; `claimed` // is the high-water mark so claims are idempotent. @@ -169,6 +227,17 @@ persistent actor Bridge { public query func getDex() : async ?Principal { cachedDex() }; public query func getSupportedAssets() : async [Text] { ASSETS }; + // The Bridge's own posture, readable the same way and with the same + // spelling as the DEX's getDeployMode. Deploy scripts and the pre-mainnet + // checklist can now assert that the two agree instead of assuming it. + public query func getDeployMode() : async Text { + switch (DEPLOY_MODE) { + case (#dev) { "dev" }; + case (#play) { "play" }; + case (#production) { "production" }; + }; + }; + // ── Addresses ─────────────────────────────────────────────── public type ChainAddress = { chain : Text; asset : Text; address : Text }; @@ -228,11 +297,24 @@ persistent actor Bridge { // across the await — the real Bridge needs the same saga discipline). public shared (msg) func claim(asset : Text) : async { #ok : Nat; #err : Text } { requireAuth(msg.caller); + // FIRST statement after auth: an unvalidated asset must never reach a map + // key. Without this, claim("") was a permanent write. + if (not isSupportedAsset(asset)) { return #err("Unsupported asset: " # asset) }; + switch (requireNotProduction("claim")) { case (#err(e)) { return #err(e) }; case (#ok) {} }; let dexP = switch (effectiveDex()) { case (?p) { p }; case null { return #err("Bridge is not wired to a DEX yet") } }; let k = key(msg.caller, asset); if (Map.get(claiming, Text.compare, k) != null) { return #err("A claim for " # asset # " is already in progress") }; - let l = ledgerOf(msg.caller, asset); + // READ-ONLY lookup, deliberately NOT ledgerOf. ledgerOf is get-or-CREATE, + // and returning #err from an update method is a NORMAL RETURN, not a trap + // — so the Map.add COMMITS. Calling it above the claimable==0 rejection + // meant every no-op claim wrote a permanent ledger row. An absent row is + // definitionally confirmed = claimed = 0, so it takes the same branch and + // the caller sees the identical message; it just costs no state. + let l = switch (Map.get(ledgers, Text.compare, k)) { + case (?x) { x }; + case null { return #err("Nothing to claim for " # asset) }; + }; let claimable = if (l.confirmed > l.claimed) { l.confirmed - l.claimed } else { 0 }; if (claimable == 0) { return #err("Nothing to claim for " # asset) }; @@ -269,6 +351,10 @@ persistent actor Bridge { // DEX no-ops instead of double-debiting the allowance. public shared (msg) func devSimulateDeposit(asset : Text, amount : Nat) : async { #ok; #err : Text } { requireAuth(msg.caller); + // Validate the asset BEFORE anything can key a map with it (`admitting`, + // `admittedUnits` and `ledgers` are all keyed on it below). + if (not isSupportedAsset(asset)) { return #err("Unsupported asset: " # asset) }; + switch (requireNotProduction("devSimulateDeposit")) { case (#err(e)) { return #err(e) }; case (#ok) {} }; if (amount == 0) { return #err("Amount must be positive") }; switch (effectiveDex()) { case (?dexP) { @@ -297,6 +383,12 @@ persistent actor Bridge { }; public shared (msg) func devConfirmDeposits() : async () { requireAuth(msg.caller); + // No Result channel in this signature, so the posture refusal is a TRAP — + // loud, per the rule above. (It iterates ASSETS, so it needs no asset + // validation of its own: it can only ever touch supported keys.) + if (IS_PRODUCTION) { + Runtime.trap("devConfirmDeposits is disabled on #production: this Bridge is a STUB with no custody behind it. Deploy the real chain-key Bridge before flipping the posture."); + }; for (asset in ASSETS.vals()) { let l = ledgerOf(msg.caller, asset); if (l.pending > 0) { l.confirmed += l.pending; l.pending := 0 }; diff --git a/src/frontend/.ic-assets.json5 b/src/frontend/.ic-assets.json5 deleted file mode 100644 index 1e52851..0000000 --- a/src/frontend/.ic-assets.json5 +++ /dev/null @@ -1,29 +0,0 @@ -[ - { - "match": "**/*", - "security_policy": "standard", - "headers": { - "Cache-Control": "public, max-age=0, must-revalidate" - }, - "allow_raw_access": false - }, - { - "match": "assets/**/*", - "headers": { - "Cache-Control": "public, max-age=31536000, immutable" - } - }, - { - // IC App Connect bridge page (ai-connect.html): forbid framing so a - // clickjacking overlay can't trick the user into approving the II sign-in. - // The page self-enforces too; this header covers older browsers/proxies. - "match": "ai-connect.html", - "headers": { - "Content-Security-Policy": "frame-ancestors 'none'" - } - }, - { - "match": "**/*", - "enable_aliasing": true - } -] diff --git a/src/frontend/index.html b/src/frontend/index.html index ed48353..e962ee4 100644 --- a/src/frontend/index.html +++ b/src/frontend/index.html @@ -50,6 +50,11 @@ .logo-shimmer: one-shot light sweep when the app reveals. --> + + @@ -66,8 +71,10 @@ - + and the insurance fund is a destination in its own right. The + arrow marks it as a redirect into Account rather than a page of + its own. --> + +
+ + + + + + + + +
- VERSION 1.50 + +
@@ -1035,6 +1063,11 @@

Exchange Canister

The Internet Computer canister running MULTI/DEX trading ·
Why this is trustless →
+
+
Backend Version
+
+
release this wasm was built from
+
Compute Fuel (cycles)
@@ -2103,10 +2136,14 @@

Sign in

- - + diff --git a/src/frontend/public/.ic-assets.json5 b/src/frontend/public/.ic-assets.json5 index d433712..f3b1d48 100644 --- a/src/frontend/public/.ic-assets.json5 +++ b/src/frontend/public/.ic-assets.json5 @@ -19,8 +19,17 @@ // names the content-hashed bundle), and refuse raw.icp0.io, whose responses // BYPASS certification: a raw URL can serve modified JS to a signed-in // trader with no cryptographic check. + // + // security_policy "standard" is what actually emits a Content-Security-Policy + // (plus X-Content-Type-Options, Referrer-Policy, X-Frame-Options and friends). + // Without it the deployed app shipped NO CSP at all — certified assets prove + // the bytes came from the canister, but say nothing about what those bytes are + // then allowed to load. `script-src 'self'` is only affordable because the app + // now bundles every script it runs (the lightweight-charts CDN