aboutsummaryrefslogtreecommitdiff
path: root/aoc2023/build/packages/gleam_erlang
diff options
context:
space:
mode:
Diffstat (limited to 'aoc2023/build/packages/gleam_erlang')
-rw-r--r--aoc2023/build/packages/gleam_erlang/LICENSE191
-rw-r--r--aoc2023/build/packages/gleam_erlang/README.md37
-rw-r--r--aoc2023/build/packages/gleam_erlang/gleam.toml18
-rw-r--r--aoc2023/build/packages/gleam_erlang/include/gleam@erlang@file_FileInfo.hrl15
-rw-r--r--aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_Abnormal.hrl1
-rw-r--r--aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_CalleeDown.hrl1
-rw-r--r--aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_Cancelled.hrl1
-rw-r--r--aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_ExitMessage.hrl4
-rw-r--r--aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_ProcessDown.hrl4
-rw-r--r--aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_ProcessMonitor.hrl1
-rw-r--r--aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_Subject.hrl4
-rw-r--r--aoc2023/build/packages/gleam_erlang/include/gleam@erlang_ApplicationFailedToStart.hrl4
-rw-r--r--aoc2023/build/packages/gleam_erlang/include/gleam@erlang_UnknownApplication.hrl1
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam/erlang.gleam158
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam/erlang/atom.gleam79
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam/erlang/charlist.gleam25
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam/erlang/file.gleam737
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam/erlang/node.gleam62
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam/erlang/os.gleam95
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam/erlang/process.gleam744
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam@erlang.erl90
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam@erlang@atom.erl26
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam@erlang@charlist.erl15
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam@erlang@file.erl190
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam@erlang@node.erl33
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam@erlang@os.erl27
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam@erlang@process.erl374
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam_erlang.app.src14
-rw-r--r--aoc2023/build/packages/gleam_erlang/src/gleam_erlang_ffi.erl263
29 files changed, 3214 insertions, 0 deletions
diff --git a/aoc2023/build/packages/gleam_erlang/LICENSE b/aoc2023/build/packages/gleam_erlang/LICENSE
new file mode 100644
index 0000000..59e1345
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/LICENSE
@@ -0,0 +1,191 @@
+ 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
+
+ Copyright {{copyright_year}}, {{author_name}} <{{author_email}}>.
+
+ 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/aoc2023/build/packages/gleam_erlang/README.md b/aoc2023/build/packages/gleam_erlang/README.md
new file mode 100644
index 0000000..ffee4cd
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/README.md
@@ -0,0 +1,37 @@
+# Gleam Erlang 🐙
+
+A library for making use of Erlang specific code!
+
+## Features
+
+- Typed Erlang processes and message sending.
+- Erlang binary format (de)serialisation.
+- Functions for working with Erlang's charlists.
+- Reading, writing, and deletion of files.
+- Basic distributed Erlang support and working with nodes.
+- Reading and writing of environment variables.
+- Functions for working with atoms.
+
+## Usage
+
+Add this library to your Gleam project
+
+```shell
+gleam add gleam_erlang
+```
+
+And then use it in your code
+
+```gleam
+import gleam/io
+import gleam/erlang/file
+
+pub fn main() {
+ assert Ok(contents) = file.read("pokedex.txt")
+ io.println(contents)
+}
+```
+
+Documentation can be found at <https://hexdocs.pm/gleam_erlang/>.
+
+This library requires OTP 23.0 or higher.
diff --git a/aoc2023/build/packages/gleam_erlang/gleam.toml b/aoc2023/build/packages/gleam_erlang/gleam.toml
new file mode 100644
index 0000000..8d62603
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/gleam.toml
@@ -0,0 +1,18 @@
+name = "gleam_erlang"
+
+version = "0.23.1"
+licences = ["Apache-2.0"]
+description = "A Gleam library for working with Erlang"
+
+repository = { type = "github", user = "gleam-lang", repo = "erlang" }
+links = [
+ { title = "Website", href = "https://gleam.run" },
+ { title = "Sponsor", href = "https://github.com/sponsors/lpil" },
+]
+gleam = ">= 0.32.0"
+
+[dependencies]
+gleam_stdlib = "~> 0.32"
+
+[dev-dependencies]
+gleeunit = "~> 0.6"
diff --git a/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@file_FileInfo.hrl b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@file_FileInfo.hrl
new file mode 100644
index 0000000..b38d11e
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@file_FileInfo.hrl
@@ -0,0 +1,15 @@
+-record(file_info, {
+ size :: integer(),
+ file_type :: gleam@erlang@file:file_type(),
+ access :: gleam@erlang@file:access(),
+ atime :: integer(),
+ mtime :: integer(),
+ ctime :: integer(),
+ mode :: integer(),
+ links :: integer(),
+ major_device :: integer(),
+ minor_device :: integer(),
+ inode :: integer(),
+ user_id :: integer(),
+ group_id :: integer()
+}).
diff --git a/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_Abnormal.hrl b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_Abnormal.hrl
new file mode 100644
index 0000000..4cd0452
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_Abnormal.hrl
@@ -0,0 +1 @@
+-record(abnormal, {reason :: binary()}).
diff --git a/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_CalleeDown.hrl b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_CalleeDown.hrl
new file mode 100644
index 0000000..5dd5047
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_CalleeDown.hrl
@@ -0,0 +1 @@
+-record(callee_down, {reason :: gleam@dynamic:dynamic_()}).
diff --git a/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_Cancelled.hrl b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_Cancelled.hrl
new file mode 100644
index 0000000..b82b49f
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_Cancelled.hrl
@@ -0,0 +1 @@
+-record(cancelled, {time_remaining :: integer()}).
diff --git a/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_ExitMessage.hrl b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_ExitMessage.hrl
new file mode 100644
index 0000000..c476308
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_ExitMessage.hrl
@@ -0,0 +1,4 @@
+-record(exit_message, {
+ pid :: gleam@erlang@process:pid_(),
+ reason :: gleam@erlang@process:exit_reason()
+}).
diff --git a/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_ProcessDown.hrl b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_ProcessDown.hrl
new file mode 100644
index 0000000..df0b6b7
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_ProcessDown.hrl
@@ -0,0 +1,4 @@
+-record(process_down, {
+ pid :: gleam@erlang@process:pid_(),
+ reason :: gleam@dynamic:dynamic_()
+}).
diff --git a/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_ProcessMonitor.hrl b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_ProcessMonitor.hrl
new file mode 100644
index 0000000..ce552e2
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_ProcessMonitor.hrl
@@ -0,0 +1 @@
+-record(process_monitor, {tag :: gleam@erlang:reference_()}).
diff --git a/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_Subject.hrl b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_Subject.hrl
new file mode 100644
index 0000000..abc46b2
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang@process_Subject.hrl
@@ -0,0 +1,4 @@
+-record(subject, {
+ owner :: gleam@erlang@process:pid_(),
+ tag :: gleam@erlang:reference_()
+}).
diff --git a/aoc2023/build/packages/gleam_erlang/include/gleam@erlang_ApplicationFailedToStart.hrl b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang_ApplicationFailedToStart.hrl
new file mode 100644
index 0000000..52c9896
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang_ApplicationFailedToStart.hrl
@@ -0,0 +1,4 @@
+-record(application_failed_to_start, {
+ name :: gleam@erlang@atom:atom_(),
+ reason :: gleam@dynamic:dynamic_()
+}).
diff --git a/aoc2023/build/packages/gleam_erlang/include/gleam@erlang_UnknownApplication.hrl b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang_UnknownApplication.hrl
new file mode 100644
index 0000000..fde3c61
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/include/gleam@erlang_UnknownApplication.hrl
@@ -0,0 +1 @@
+-record(unknown_application, {name :: gleam@erlang@atom:atom_()}).
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam/erlang.gleam b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang.gleam
new file mode 100644
index 0000000..783cd53
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang.gleam
@@ -0,0 +1,158 @@
+import gleam/dynamic.{type Dynamic}
+import gleam/list
+import gleam/erlang/atom.{type Atom}
+import gleam/erlang/charlist.{type Charlist}
+
+@external(erlang, "io_lib", "format")
+fn erl_format(a: String, b: List(a)) -> Charlist
+
+/// Return a string representation of any term
+pub fn format(term: any) -> String {
+ charlist.to_string(erl_format("~p", [term]))
+}
+
+@external(erlang, "erlang", "term_to_binary")
+pub fn term_to_binary(a: a) -> BitArray
+
+type Safe {
+ Safe
+}
+
+@external(erlang, "erlang", "binary_to_term")
+fn erl_binary_to_term(a: BitArray, b: List(Safe)) -> Dynamic
+
+pub fn binary_to_term(binary: BitArray) -> Result(Dynamic, Nil) {
+ case rescue(fn() { erl_binary_to_term(binary, [Safe]) }) {
+ Ok(term) -> Ok(term)
+ Error(_) -> Error(Nil)
+ }
+}
+
+pub fn unsafe_binary_to_term(binary: BitArray) -> Result(Dynamic, Nil) {
+ case rescue(fn() { erl_binary_to_term(binary, []) }) {
+ Ok(term) -> Ok(term)
+ Error(_) -> Error(Nil)
+ }
+}
+
+/// Error value returned by `get_line` function
+///
+pub type GetLineError {
+ Eof
+ NoData
+}
+
+/// Reads a line from standard input with the given prompt.
+///
+/// # Example
+///
+/// > get_line("Language: ")
+/// // -> Language: <- gleam
+/// Ok("gleam\n")
+///
+@external(erlang, "gleam_erlang_ffi", "get_line")
+pub fn get_line(prompt prompt: String) -> Result(String, GetLineError)
+
+pub type TimeUnit {
+ Second
+ Millisecond
+ Microsecond
+ Nanosecond
+}
+
+/// Returns the current OS system time.
+///
+/// <https://erlang.org/doc/apps/erts/time_correction.html#OS_System_Time>
+@external(erlang, "os", "system_time")
+pub fn system_time(a: TimeUnit) -> Int
+
+/// Returns the current OS system time as a tuple of Ints
+///
+/// http://erlang.org/doc/man/os.html#timestamp-0
+@external(erlang, "os", "timestamp")
+pub fn erlang_timestamp() -> #(Int, Int, Int)
+
+/// Gleam doesn't offer any way to raise exceptions, but they may still occur
+/// due to bugs when working with unsafe code, such as when calling Erlang
+/// function.
+///
+/// This function will catch any error thrown and convert it into a result
+/// rather than crashing the process.
+///
+@external(erlang, "gleam_erlang_ffi", "rescue")
+pub fn rescue(a: fn() -> a) -> Result(a, Crash)
+
+pub type Crash {
+ Exited(Dynamic)
+ Thrown(Dynamic)
+ Errored(Dynamic)
+}
+
+@external(erlang, "init", "get_plain_arguments")
+fn get_start_arguments() -> List(Charlist)
+
+/// Get the arguments given to the program when it was started.
+///
+/// This is sometimes called `argv` in other languages.
+pub fn start_arguments() -> List(String) {
+ get_start_arguments()
+ |> list.map(charlist.to_string)
+}
+
+/// Starts an OTP application's process tree in the background, as well as
+/// the trees of any applications that the given application depends upon. An
+/// OTP application typically maps onto a Gleam or Hex package.
+///
+/// Returns a list of the applications that were started. Calling this function
+/// for application that have already been started is a no-op so you do not need
+/// to check the application state beforehand.
+///
+/// In Gleam we prefer to not use these implicit background process trees, but
+/// you will likely still need to start the trees of OTP applications written in
+/// other BEAM languages such as Erlang or Elixir, including those included by
+/// default with Erlang/OTP.
+///
+/// For more information see the OTP documentation.
+/// - <https://www.erlang.org/doc/man/application.html#ensure_all_started-1>
+/// - <https://www.erlang.org/doc/man/application.html#start-1>
+///
+@external(erlang, "gleam_erlang_ffi", "ensure_all_started")
+pub fn ensure_all_started(
+ application application: Atom,
+) -> Result(List(Atom), EnsureAllStartedError)
+
+pub type EnsureAllStartedError {
+ UnknownApplication(name: Atom)
+ ApplicationFailedToStart(name: Atom, reason: Dynamic)
+}
+
+/// A unique reference value.
+///
+/// It holds no particular meaning or value, but unique values are often useful
+/// in programs are used heavily within both Gleam and Erlang's OTP frameworks.
+///
+/// More can be read about references in the [Erlang documentation][1].
+///
+/// [1]: https://www.erlang.org/doc/efficiency_guide/advanced.html#unique_references
+///
+pub type Reference
+
+/// Create a new unique reference.
+///
+@external(erlang, "erlang", "make_ref")
+pub fn make_reference() -> Reference
+
+/// Returns the path of a package's `priv` directory, where extra non-Gleam
+/// or Erlang files are typically kept.
+///
+/// Returns an error if no package was found with the given name.
+///
+/// # Example
+///
+/// ```gleam
+/// > erlang.priv_directory("my_app")
+/// // -> Ok("/some/location/my_app/priv")
+/// ```
+///
+@external(erlang, "gleam_erlang_ffi", "priv_directory")
+pub fn priv_directory(name: String) -> Result(String, Nil)
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/atom.gleam b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/atom.gleam
new file mode 100644
index 0000000..a27289c
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/atom.gleam
@@ -0,0 +1,79 @@
+import gleam/dynamic.{type DecodeErrors, type Dynamic}
+
+/// Atom is a special string-like data-type that is most commonly used for
+/// interfacing with code written in other BEAM languages such as Erlang and
+/// Elixir. It is preferable to define your own custom types to use instead of
+/// atoms where possible.
+///
+/// Atoms are not used much in typical Gleam code!
+///
+/// ## Creating atoms
+///
+/// We can create atoms with the the [`create_from_string`](#create_from_string)
+/// function, though we must be careful when doing so as atoms are never
+/// garbage collected. If we create too many atoms (for example, if we convert
+/// user input into atoms) we may hit the max limit of atoms and cause the
+/// virtual machine to crash.
+///
+pub type Atom
+
+/// An error returned when no atom is found in the virtual machine's atom table
+/// for a given string when calling the [`from_string`](#from_string) function.
+pub type FromStringError {
+ AtomNotLoaded
+}
+
+/// Finds an existing Atom for the given String.
+///
+/// If no atom is found in the virtual machine's atom table for the String then
+/// an error is returned.
+///
+/// ## Examples
+///
+/// > from_string("ok")
+/// Ok(create_from_string("ok"))
+///
+/// > from_string("some_new_atom")
+/// Error(AtomNotLoaded)
+///
+@external(erlang, "gleam_erlang_ffi", "atom_from_string")
+pub fn from_string(a: String) -> Result(Atom, FromStringError)
+
+/// Creates an atom from a string, inserting a new value into the virtual
+/// machine's atom table if an atom does not already exist for the given
+/// string.
+///
+/// We must be careful when using this function as there is a limit to the
+/// number of atom that can fit in the virtual machine's atom table. Never
+/// convert user input into atoms as filling the atom table will cause the
+/// virtual machine to crash!
+///
+@external(erlang, "erlang", "binary_to_atom")
+pub fn create_from_string(a: String) -> Atom
+
+/// Returns a `String` corresponding to the text representation of the given
+/// `Atom`.
+///
+/// ## Examples
+///
+/// > let ok_atom = create_from_string("ok")
+/// > to_string(ok_atom)
+/// "ok"
+///
+@external(erlang, "erlang", "atom_to_binary")
+pub fn to_string(a: Atom) -> String
+
+/// Checks to see whether a `Dynamic` value is an atom, and return the atom if
+/// it is.
+///
+/// ## Examples
+///
+/// > import gleam/dynamic
+/// > from_dynamic(dynamic.from(create_from_string("hello")))
+/// Ok(create_from_string("hello"))
+///
+/// > from_dynamic(dynamic.from(123))
+/// Error([DecodeError(expected: "Atom", found: "Int", path: [])])
+///
+@external(erlang, "gleam_erlang_ffi", "atom_from_dynamic")
+pub fn from_dynamic(from from: Dynamic) -> Result(Atom, DecodeErrors)
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/charlist.gleam b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/charlist.gleam
new file mode 100644
index 0000000..e5b6d65
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/charlist.gleam
@@ -0,0 +1,25 @@
+//// A charlist is a list of integers where all the integers are valid code
+//// points.
+////
+//// In practice, you will not come across them often, except perhaps when
+//// interfacing with Erlang, in particular when using older libraries that do
+//// not accept binaries as arguments.
+
+/// A list of characters represented as ints. Commonly used by older Erlang
+/// modules.
+pub type Charlist
+
+/// Transform a charlist to a string
+@external(erlang, "unicode", "characters_to_binary")
+pub fn to_string(a: Charlist) -> String
+
+// Calls `unicode:characters_to_binary(Data, unicode, unicode)`
+// Note: `unicode is an alias for utf8`
+// See <https://www.erlang.org/doc/man/unicode.html#characters_to_binary-1>
+
+/// Transform a string to a charlist
+@external(erlang, "unicode", "characters_to_list")
+pub fn from_string(a: String) -> Charlist
+// Calls `unicode:characters_to_list(Data, unicode)`
+// Note: `unicode is an alias for utf8`
+// See <https://www.erlang.org/doc/man/unicode.html#characters_to_list-1>
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/file.gleam b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/file.gleam
new file mode 100644
index 0000000..48e11a7
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/file.gleam
@@ -0,0 +1,737 @@
+//// Working with files on the filesystem.
+////
+//// The functions included in this module are for high-level concepts such as
+//// reading and writing.
+
+import gleam/bit_array
+import gleam/result
+
+/// Reason represents all of the reasons that Erlang surfaces of why a file
+/// system operation could fail. Most of these reasons are POSIX errors, which
+/// come from the operating system and start with `E`. Others have been added to
+/// represent other issues that may arise.
+pub type Reason {
+ /// Permission denied.
+ Eacces
+ /// Resource temporarily unavailable.
+ Eagain
+ /// Bad file number
+ Ebadf
+ /// Bad message.
+ Ebadmsg
+ /// File busy.
+ Ebusy
+ /// Resource deadlock avoided.
+ Edeadlk
+ /// On most architectures, same as `Edeadlk`. On some architectures, it
+ /// means "File locking deadlock error."
+ Edeadlock
+ /// Disk quota exceeded.
+ Edquot
+ /// File already exists.
+ Eexist
+ /// Bad address in system call argument.
+ Efault
+ /// File too large.
+ Efbig
+ /// Inappropriate file type or format. Usually caused by trying to set the
+ /// "sticky bit" on a regular file (not a directory).
+ Eftype
+ /// Interrupted system call.
+ Eintr
+ /// Invalid argument.
+ Einval
+ /// I/O error.
+ Eio
+ /// Illegal operation on a directory.
+ Eisdir
+ /// Too many levels of symbolic links.
+ Eloop
+ /// Too many open files.
+ Emfile
+ /// Too many links.
+ Emlink
+ /// Multihop attempted.
+ Emultihop
+ /// Filename too long
+ Enametoolong
+ /// File table overflow
+ Enfile
+ /// No buffer space available.
+ Enobufs
+ /// No such device.
+ Enodev
+ /// No locks available.
+ Enolck
+ /// Link has been severed.
+ Enolink
+ /// No such file or directory.
+ Enoent
+ /// Not enough memory.
+ Enomem
+ /// No space left on device.
+ Enospc
+ /// No STREAM resources.
+ Enosr
+ /// Not a STREAM.
+ Enostr
+ /// Function not implemented.
+ Enosys
+ /// Block device required.
+ Enotblk
+ /// Not a directory.
+ Enotdir
+ /// Operation not supported.
+ Enotsup
+ /// No such device or address.
+ Enxio
+ /// Operation not supported on socket.
+ Eopnotsupp
+ /// Value too large to be stored in data type.
+ Eoverflow
+ /// Not owner.
+ Eperm
+ /// Broken pipe.
+ Epipe
+ /// Result too large.
+ Erange
+ /// Read-only file system.
+ Erofs
+ /// Invalid seek.
+ Espipe
+ /// No such process.
+ Esrch
+ /// Stale remote file handle.
+ Estale
+ /// Text file busy.
+ Etxtbsy
+ /// Cross-domain link.
+ Exdev
+ /// File was requested to be read as UTF-8, but is not UTF-8 encoded.
+ NotUtf8
+}
+
+/// The type of file found by `file_info` or `link_info`.
+///
+pub type FileType {
+ Device
+ Directory
+ Other
+ Regular
+ Symlink
+}
+
+/// The read/write permissions a user can have for a file.
+///
+pub type Access {
+ NoAccess
+ Read
+ ReadWrite
+ Write
+}
+
+/// Meta information for a file.
+///
+/// Timestamps are in seconds before or after the Unix time epoch,
+/// `1970-01-01 00:00:00 UTC`.
+///
+pub type FileInfo {
+ FileInfo(
+ /// File size in bytes.
+ ///
+ size: Int,
+ /// `Regular`, `Directory`, `Symlink`, `Device`, or `Other`.
+ ///
+ file_type: FileType,
+ /// `ReadWrite`, `Read`, `Write`, or `NoAccess`.
+ ///
+ access: Access,
+ /// Timestamp of most recent access.
+ ///
+ atime: Int,
+ /// Timestamp of most recent modification.
+ ///
+ mtime: Int,
+ /// Timestamp of most recent change (or file creation, depending on
+ /// operating system).
+ ///
+ ctime: Int,
+ /// File permissions encoded as a sum of bit values, including but not
+ /// limited to:
+ ///
+ /// Owner read, write, execute.
+ ///
+ /// `0o400`, `0o200`, `0o100`
+ ///
+ /// Group read, write, execute.
+ ///
+ /// `0o40`, `0o20`, `0o10`
+ ///
+ /// Other read, write, execute.
+ ///
+ /// `0o4`, `0o2`, `0o1`
+ ///
+ /// Set user ID, group ID on execution.
+ ///
+ /// `0x800`, `0x400`
+ ///
+ mode: Int,
+ /// Total links to a file (always `1` for file systems without links).
+ ///
+ links: Int,
+ /// The file system where a file is located (`0` for drive `A:` on Windows,
+ /// `1` for `B:`, etc.).
+ ///
+ major_device: Int,
+ /// Character device (or `0` on non-Unix systems).
+ ///
+ minor_device: Int,
+ /// The `inode` number for a file (always `0` on non-Unix file systems).
+ ///
+ inode: Int,
+ /// The owner of a file (always `0` on non-Unix file systems).
+ ///
+ user_id: Int,
+ /// The group id of a file (always `0` on non-Unix file systems).
+ ///
+ group_id: Int,
+ )
+}
+
+/// Results in `FileInfo` about the given `path` on success, otherwise a
+/// `Reason` for failure.
+///
+/// When `path` refers to a symlink, the result pertains to the link's target.
+/// To get `FileInfo` about a symlink itself, use `link_info`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > file_info("gleam.toml")
+/// Ok(FileInfo(
+/// size: 430,
+/// file_type: Regular,
+/// access: ReadWrite,
+/// atime: 1680580321,
+/// mtime: 1680580272,
+/// ctime: 1680580272,
+/// mode: 33188,
+/// links: 1,
+/// major_device: 64,
+/// minor_device: 0,
+/// inode: 469028,
+/// user_id: 1000,
+/// group_id: 1000,
+/// ))
+///
+/// > file_info("/root")
+/// Ok(FileInfo(
+/// size: 16,
+/// file_type: Directory,
+/// access: Read,
+/// atime: 1677789967,
+/// mtime: 1664561240,
+/// ctime: 1664561240,
+/// mode: 16877,
+/// links: 11,
+/// major_device: 54,
+/// minor_device: 0,
+/// inode: 34,
+/// user_id: 0,
+/// group_id: 0,
+/// ))
+///
+/// > file_info("./build/dev/erlang/rad/priv")
+/// Ok(FileInfo(
+/// size: 140,
+/// file_type: Directory,
+/// access: ReadWrite,
+/// atime: 1680580321,
+/// mtime: 1680580272,
+/// ctime: 1680580272,
+/// mode: 33188,
+/// links: 1,
+/// major_device: 64,
+/// minor_device: 0,
+/// inode: 469028,
+/// user_id: 1000,
+/// group_id: 1000,
+/// ))
+///
+/// > file_info("/does_not_exist")
+/// Error(Enoent)
+///
+/// > file_info("/root/.local/maybe_exists")
+/// Error(Eacces)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+pub fn file_info(a: String) -> Result(FileInfo, Reason) {
+ do_file_info(a)
+}
+
+@external(erlang, "gleam_erlang_ffi", "file_info")
+fn do_file_info(a: String) -> Result(FileInfo, Reason)
+
+/// Results in `FileInfo` about the given `path` on success, otherwise a
+/// `Reason` for failure.
+///
+/// When `path` refers to a symlink, the result pertains to the link itself.
+/// To get `FileInfo` about a symlink's target, use `file_info`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > link_info("gleam.toml")
+/// Ok(FileInfo(
+/// size: 430,
+/// file_type: Regular,
+/// access: ReadWrite,
+/// atime: 1680580321,
+/// mtime: 1680580272,
+/// ctime: 1680580272,
+/// mode: 33188,
+/// links: 1,
+/// major_device: 64,
+/// minor_device: 0,
+/// inode: 469028,
+/// user_id: 1000,
+/// group_id: 1000,
+/// ))
+///
+/// > link_info("/root")
+/// Ok(FileInfo(
+/// size: 16,
+/// file_type: Directory,
+/// access: Read,
+/// atime: 1677789967,
+/// mtime: 1664561240,
+/// ctime: 1664561240,
+/// mode: 16877,
+/// links: 11,
+/// major_device: 54,
+/// minor_device: 0,
+/// inode: 34,
+/// user_id: 0,
+/// group_id: 0,
+/// ))
+///
+/// > link_info("./build/dev/erlang/rad/priv")
+/// Ok(FileInfo(
+/// size: 41,
+/// file_type: Symlink,
+/// access: ReadWrite,
+/// atime: 1680581150,
+/// mtime: 1680581150,
+/// ctime: 1680581150,
+/// mode: 41471,
+/// links: 1,
+/// major_device: 64,
+/// minor_device: 0,
+/// inode: 471587,
+/// user_id: 1000,
+/// group_id: 1000,
+/// ))
+///
+/// > link_info("/does_not_exist")
+/// Error(Enoent)
+///
+/// > link_info("/root/.local/maybe_exists")
+/// Error(Eacces)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+pub fn link_info(a: String) -> Result(FileInfo, Reason) {
+ do_link_info(a)
+}
+
+@external(erlang, "gleam_erlang_ffi", "link_info")
+fn do_link_info(a: String) -> Result(FileInfo, Reason)
+
+/// Results in a `Bool` on success that indicates whether the given `path` has
+/// a `Directory` `FileType`, otherwise a `Reason` for failure.
+///
+/// When `path` refers to a symlink, the result pertains to the link's target.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > is_directory("/tmp")
+/// Ok(True)
+///
+/// > is_directory("resume.pdf")
+/// Ok(False)
+///
+/// > is_directory("/does_not_exist")
+/// Error(Enoent)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+pub fn is_directory(path: String) -> Result(Bool, Reason) {
+ use FileInfo(file_type: file_type, ..) <- result.map(over: do_file_info(path))
+ file_type == Directory
+}
+
+/// Results in a `Bool` on success that indicates whether the given `path` has
+/// a `Regular` `FileType`, otherwise a `Reason` for failure.
+///
+/// When `path` refers to a symlink, the result pertains to the link's target.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > is_regular("resume.pdf")
+/// Ok(True)
+///
+/// > is_regular("/tmp")
+/// Ok(False)
+///
+/// > is_regular("/does_not_exist.txt")
+/// Error(Enoent)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+pub fn is_regular(path: String) -> Result(Bool, Reason) {
+ use FileInfo(file_type: file_type, ..) <- result.map(over: do_file_info(path))
+ file_type == Regular
+}
+
+/// Results in a `Bool` on success that indicates whether the given `path`
+/// exists, otherwise a `Reason` for failure.
+///
+/// When `path` refers to a symlink, the result pertains to the link's target.
+/// To find whether a symlink itself exists, use `link_exists`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > file_exists("resume.pdf")
+/// Ok(True)
+///
+/// > file_exists("/tmp")
+/// Ok(True)
+///
+/// > file_exists("/does_not_exist")
+/// Ok(False)
+///
+/// > file_exists("/root/.local/maybe_exists")
+/// Error(Eacces)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+pub fn file_exists(path: String) -> Result(Bool, Reason) {
+ let result =
+ path
+ |> do_file_info
+ |> result.replace(True)
+ case result {
+ Error(Enoent) -> Ok(False)
+ _ -> result
+ }
+}
+
+/// Results in a `Bool` on success that indicates whether the given `path`
+/// exists, otherwise a `Reason` for failure.
+///
+/// When `path` refers to a symlink, the result pertains to the link itself.
+/// To find whether a symlink's target exists, use `file_exists`.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > link_exists("resume.pdf")
+/// Ok(True)
+///
+/// > link_exists("/tmp")
+/// Ok(True)
+///
+/// > link_exists("/does_not_exist")
+/// Ok(False)
+///
+/// > link_exists("/root/.local/maybe_exists")
+/// Error(Eacces)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+pub fn link_exists(path: String) -> Result(Bool, Reason) {
+ let result =
+ path
+ |> do_link_info
+ |> result.replace(True)
+ case result {
+ Error(Enoent) -> Ok(False)
+ _ -> result
+ }
+}
+
+/// Tries to create a directory. Missing parent directories are not created.
+///
+/// Returns a Result of nil if the directory is created or Reason if the
+/// operation failed.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > make_directory("/tmp/foo")
+/// Ok(Nil)
+///
+/// > make_directory("relative_directory")
+/// Ok(Nil)
+///
+/// > make_directory("/tmp/missing_intermediate_directory/foo")
+/// Error(Enoent)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+@external(erlang, "gleam_erlang_ffi", "make_directory")
+pub fn make_directory(a: String) -> Result(Nil, Reason)
+
+/// Lists all files in a directory, except files with
+/// [raw filenames](https://www.erlang.org/doc/apps/stdlib/unicode_usage.html#notes-about-raw-filenames).
+///
+/// Returns a Result containing the list of filenames in the directory, or Reason
+/// if the operation failed.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > list_directory("/tmp")
+/// Ok(["FB01293B-8597-4359-80D5-130140A0C0DE","AlTest2.out"])
+///
+/// > list_directory("resume.docx")
+/// Error(Enotdir)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+@external(erlang, "gleam_erlang_ffi", "list_directory")
+pub fn list_directory(a: String) -> Result(List(String), Reason)
+
+/// Deletes a directory.
+///
+/// The directory must be empty before it can be deleted. Returns a nil Success
+/// or Reason if the operation failed.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > delete_directory("foo")
+/// Ok(Nil)
+///
+/// > delete_directory("does_not_exist/")
+/// Error(Enoent)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+@external(erlang, "gleam_erlang_ffi", "delete_directory")
+pub fn delete_directory(a: String) -> Result(Nil, Reason)
+
+/// Deletes a file or directory recursively.
+///
+/// Returns a nil Success or Reason if the operation failed.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > recursive_delete("foo")
+/// Ok(Nil)
+///
+/// > recursive_delete("/bar")
+/// Ok(Nil)
+///
+/// > recursive_delete("does_not_exist/")
+/// Error(Enoent)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+@external(erlang, "gleam_erlang_ffi", "recursive_delete")
+pub fn recursive_delete(a: String) -> Result(Nil, Reason)
+
+/// Read the contents of the given file as a String
+///
+/// Assumes the file is UTF-8 encoded. Returns a Result containing the file's
+/// contents as a String if the operation was successful, or Reason if the file
+/// operation failed. If the file is not UTF-8 encoded, the `NotUTF8` variant
+/// will be returned.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > read("example.txt")
+/// Ok("Hello, World!")
+///
+/// > read(from: "example.txt")
+/// Ok("Hello, World!")
+///
+/// > read("does_not_exist.txt")
+/// Error(Enoent)
+///
+/// > read("cat.gif")
+/// Error(NotUTF8)
+/// ```
+///
+@deprecated("Use the simplifile package instead?")
+pub fn read(from path: String) -> Result(String, Reason) {
+ path
+ |> do_read_bits()
+ |> result.then(fn(content) {
+ case bit_array.to_string(content) {
+ Ok(string) -> Ok(string)
+ Error(Nil) -> Error(NotUtf8)
+ }
+ })
+}
+
+/// Read the contents of the given file as a BitString
+///
+/// Returns a Result containing the file's contents as a BitString if the
+/// operation was successful, or Reason if the operation failed.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > read_bits("example.txt")
+/// Ok(<<"Hello, World!">>)
+///
+/// > read_bits(from: "cat.gif")
+/// Ok(<<71,73,70,56,57,97,1,0,1,0,0,0,0,59>>)
+///
+/// > read_bits("does_not_exist.txt")
+/// Error(Enoent)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+pub fn read_bits(from path: String) -> Result(BitArray, Reason) {
+ do_read_bits(path)
+}
+
+@external(erlang, "gleam_erlang_ffi", "read_file")
+fn do_read_bits(a: path) -> Result(BitArray, Reason)
+
+/// Write the given String contents to a file of the given name.
+///
+/// Returns a Result with Nil if the operation was successful or a Reason
+/// otherwise.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > write("Hello, World!", "file.txt")
+/// Ok(Nil)
+///
+/// > write(to: "file.txt", contents: "Hello, World!")
+/// Ok(Nil)
+///
+/// > write("Hello, World!", "does_not_exist/file.txt")
+/// Error(Enoent)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+pub fn write(contents contents: String, to path: String) -> Result(Nil, Reason) {
+ contents
+ |> bit_array.from_string
+ |> do_write_bits(path)
+}
+
+/// Write the given BitString contents to a file of the given name.
+///
+/// Returns a Result with Nil if the operation was successful or a Reason
+/// otherwise.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > write_bits(<<71,73,70,56,57,97,1,0,1,0,0,0,0,59>>, "cat.gif")
+/// Ok(Nil)
+///
+/// > write_bits(to: "cat.gif", contents: <<71,73,70,56,57,97,1,0,1,0,0,0,0,59>>)
+/// Ok(Nil)
+///
+/// > write_bits(<<71,73,70,56,57,97,1,0,1,0,0,0,0,59>>, "does_not_exist/cat.gif")
+/// Error(Enoent)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+pub fn write_bits(
+ contents contents: BitArray,
+ to path: String,
+) -> Result(Nil, Reason) {
+ do_write_bits(contents, path)
+}
+
+@external(erlang, "gleam_erlang_ffi", "write_file")
+fn do_write_bits(a: BitArray, b: String) -> Result(Nil, Reason)
+
+/// Append the given String contents to a file of the given name.
+///
+/// Returns a Result with Nil if the operation was successful or a Reason
+/// otherwise.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > append("Hello, World!", "file.txt")
+/// Ok(Nil)
+///
+/// > append(to: "file.txt", contents: "Hello, World!")
+/// Ok(Nil)
+///
+/// > append("Hello, World!", "does_not_exist/file.txt")
+/// Error(Enoent)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+pub fn append(contents contents: String, to path: String) -> Result(Nil, Reason) {
+ contents
+ |> bit_array.from_string
+ |> do_append_bits(path)
+}
+
+/// Append the given BitString contents to a file of the given name.
+///
+/// Returns a Result with Nil if the operation was successful or a Reason
+/// otherwise.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > append_bits(<<71,73,70,56,57,97,1,0,1,0,0,0,0,59>>, "cat.gif")
+/// Ok(Nil)
+///
+/// > append_bits(to: "cat.gif", contents: <<71,73,70,56,57,97,1,0,1,0,0,0,0,59>>)
+/// Ok(Nil)
+///
+/// > append_bits(<<71,73,70,56,57,97,1,0,1,0,0,0,0,59>>, "does_not_exist/cat.gif")
+/// Error(Enoent)
+/// ```
+///
+pub fn append_bits(
+ contents contents: BitArray,
+ to path: String,
+) -> Result(Nil, Reason) {
+ do_append_bits(contents, path)
+}
+
+@external(erlang, "gleam_erlang_ffi", "append_file")
+fn do_append_bits(
+ contents contents: BitArray,
+ path path: String,
+) -> Result(Nil, Reason)
+
+/// Delete the given file.
+///
+/// Returns a Result with Nil if the operation was successful or a Reason
+/// otherwise.
+///
+/// ## Examples
+///
+/// ```gleam
+/// > delete("file.txt")
+/// Ok(Nil)
+///
+/// > delete("does_not_exist.txt")
+/// Error(Enoent)
+/// ```
+///
+@deprecated("Use the simplifile package instead")
+@external(erlang, "gleam_erlang_ffi", "delete_file")
+pub fn delete(a: String) -> Result(Nil, Reason)
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/node.gleam b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/node.gleam
new file mode 100644
index 0000000..339415c
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/node.gleam
@@ -0,0 +1,62 @@
+import gleam/erlang/atom.{type Atom}
+
+pub type Node
+
+type DoNotLeak
+
+/// Return the current node.
+///
+@external(erlang, "erlang", "node")
+pub fn self() -> Node
+
+/// Return a list of all visible nodes in the cluster, not including the current
+/// node.
+///
+/// The current node can be included by calling `self()` and prepending the
+/// result.
+///
+/// ```gleam
+/// let all_nodes = [node.self(), ..node.visible()]
+/// ```
+///
+@external(erlang, "erlang", "nodes")
+pub fn visible() -> List(Node)
+
+pub type ConnectError {
+ /// Was unable to connect to the node.
+ FailedToConnect
+ /// The local node is not alive, so it is not possible to connect to the other
+ /// node.
+ LocalNodeIsNotAlive
+}
+
+// TODO: test unknown node
+// TODO: test successfully connecting
+/// Establish a connection to a node, so the nodes can send messages to each
+/// other and any other connected nodes.
+///
+/// Returns `Error(FailedToConnect)` if the node is not reachable.
+///
+/// Returns `Error(LocalNodeIsNotAlive)` if the local node is not alive, meaning
+/// it is not running in distributed mode.
+///
+@external(erlang, "gleam_erlang_ffi", "connect_node")
+pub fn connect(node: Atom) -> Result(Node, ConnectError)
+
+// TODO: test
+/// Send a message to a named process on a given node.
+///
+/// These messages are untyped, like regular Erlang messages.
+///
+pub fn send(node: Node, name: Atom, message: message) -> Nil {
+ raw_send(#(name, node), message)
+ Nil
+}
+
+@external(erlang, "erlang", "send")
+fn raw_send(receiver: #(Atom, Node), message: message) -> DoNotLeak
+
+/// Convert a node to the atom of its name.
+///
+@external(erlang, "gleam_erlang_ffi", "identity")
+pub fn to_atom(node: Node) -> Atom
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/os.gleam b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/os.gleam
new file mode 100644
index 0000000..e135974
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/os.gleam
@@ -0,0 +1,95 @@
+//// Access to the shell's environment variables
+
+import gleam/map.{type Map}
+
+/// Returns the list of all available environment variables as a list of key,
+/// tuples.
+///
+/// ## Examples
+///
+/// > get_all_env()
+/// map.from_list([
+/// #("SHELL", "/bin/bash"),
+/// #("PWD", "/home/j3rn"),
+/// ...
+/// ])
+///
+@external(erlang, "gleam_erlang_ffi", "get_all_env")
+pub fn get_all_env() -> Map(String, String)
+
+/// Returns the value associated with the given environment variable name.
+///
+/// ## Examples
+///
+/// > get_env("SHELL")
+/// "/bin/bash"
+///
+/// > get_env(name: "PWD")
+/// "/home/j3rn"
+///
+@external(erlang, "gleam_erlang_ffi", "get_env")
+pub fn get_env(name name: String) -> Result(String, Nil)
+
+/// Associates the given value with the given environment variable name.
+///
+/// ## Examples
+///
+/// > set_env("MYVAR", "MYVALUE")
+/// Nil
+/// > get_env("MYVAR")
+/// "MYVALUE"
+///
+/// > set_env(value: "MYVALUE", name: "MYVAR")
+/// Nil
+///
+@external(erlang, "gleam_erlang_ffi", "set_env")
+pub fn set_env(name name: String, value value: String) -> Nil
+
+/// Removes the environment variable with the given name.
+///
+/// Returns Nil regardless of whether the variable ever existed.
+///
+/// ## Examples
+///
+/// > get_env("MYVAR")
+/// Ok("MYVALUE")
+/// > unset_env("MYVAR")
+/// Nil
+/// > get_env("MYVAR")
+/// Error(Nil)
+///
+/// > unset_env(name: "MYVAR")
+/// Nil
+///
+@external(erlang, "gleam_erlang_ffi", "unset_env")
+pub fn unset_env(name name: String) -> Nil
+
+/// Represents operating system kernels
+pub type OsFamily {
+ // The family which includes modern versions of the Windows operating system.
+ WindowsNt
+ // The family of operating systems based on the open source Linux kernel.
+ Linux
+ // The family of Apple operating systems such as macOS and iOS.
+ Darwin
+ // The family of operating systems based on the FreeBSD kernel.
+ FreeBsd
+ // An operating system kernel other than Linux, Darwin, FreeBSD, or NT.
+ Other(String)
+}
+
+/// Returns the kernel of the host operating system.
+///
+/// Unknown kernels are reported as `Other(String)`; e.g. `Other("sunos")`.
+///
+/// ## Examples
+///
+/// > family()
+/// Linux
+/// > family()
+/// Darwin
+/// > family()
+/// Other("sunos")
+///
+@external(erlang, "gleam_erlang_ffi", "os_family")
+pub fn family() -> OsFamily
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/process.gleam b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/process.gleam
new file mode 100644
index 0000000..f660306
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam/erlang/process.gleam
@@ -0,0 +1,744 @@
+import gleam/string
+import gleam/dynamic.{type Dynamic}
+import gleam/erlang.{type Reference}
+import gleam/erlang/atom.{type Atom}
+
+/// A `Pid` (or Process identifier) is a reference to an Erlang process. Each
+/// process has a `Pid` and it is one of the lowest level building blocks of
+/// inter-process communication in the Erlang and Gleam OTP frameworks.
+///
+pub type Pid
+
+/// Get the `Pid` for the current process.
+@external(erlang, "erlang", "self")
+pub fn self() -> Pid
+
+/// Create a new Erlang process that runs concurrently to the creator. In other
+/// languages this might be called a fibre, a green thread, or a coroutine.
+///
+/// If `linked` is `True` then the created process is linked to the creator
+/// process. When a process terminates an exit signal is sent to all other
+/// processes that are linked to it, causing the process to either terminate or
+/// have to handle the signal.
+///
+/// More can be read about processes and links in the [Erlang documentation][1].
+///
+/// [1]: https://www.erlang.org/doc/reference_manual/processes.html
+///
+pub fn start(running implementation: fn() -> anything, linked link: Bool) -> Pid {
+ case link {
+ True -> spawn_link(implementation)
+ False -> spawn(implementation)
+ }
+}
+
+@external(erlang, "erlang", "spawn")
+fn spawn(a: fn() -> anything) -> Pid
+
+@external(erlang, "erlang", "spawn_link")
+fn spawn_link(a: fn() -> anything) -> Pid
+
+/// A `Subject` is a value that processes can use to send and receive messages
+/// to and from each other in a well typed way.
+///
+/// Each subject is "owned" by the process that created it. Any process can use
+/// the `send` function to sent a message of the correct type to the process
+/// that owns the subject, and the owner can use the `receive` function or the
+/// `Selector` type to receive these messages.
+///
+/// The `Subject` type is similar to the "channel" types found in other
+/// languages and the "topic" concept found in some pub-sub systems.
+///
+/// # Examples
+///
+/// ```gleam
+/// let subject = new_subject()
+///
+/// // Send a message with the subject
+/// send(subject, "Hello, Joe!")
+///
+/// // Receive the message
+/// receive(subject, within: 10)
+/// ```
+///
+pub opaque type Subject(message) {
+ Subject(owner: Pid, tag: Reference)
+}
+
+/// Create a new `Subject` owned by the current process.
+///
+pub fn new_subject() -> Subject(message) {
+ Subject(owner: self(), tag: erlang.make_reference())
+}
+
+/// Get the owner process for a `Subject`. This is the process that created the
+/// `Subject` and will receive messages sent with it.
+///
+pub fn subject_owner(subject: Subject(message)) -> Pid {
+ subject.owner
+}
+
+type DoNotLeak
+
+@external(erlang, "erlang", "send")
+fn raw_send(a: Pid, b: message) -> DoNotLeak
+
+/// Send a message to a process using a `Subject`. The message must be of the
+/// type that the `Subject` accepts.
+///
+/// This function does not wait for the `Subject` owner process to call the
+/// `receive` function, instead it returns once the message has been placed in
+/// the process' mailbox.
+///
+/// # Ordering
+///
+/// If process P1 sends two messages to process P2 it is guaranteed that process
+/// P1 will receive the messages in the order they were sent.
+///
+/// If you wish to receive the messages in a different order you can send them
+/// on two different subjects and the receiver function can call the `receive`
+/// function for each subject in the desired order, or you can write some Erlang
+/// code to perform a selective receive.
+///
+/// # Examples
+///
+/// ```gleam
+/// let subject = new_subject()
+/// send(subject, "Hello, Joe!")
+/// ```
+///
+pub fn send(subject: Subject(message), message: message) -> Nil {
+ raw_send(subject.owner, #(subject.tag, message))
+ Nil
+}
+
+/// Receive a message that has been sent to current process using the `Subject`.
+///
+/// If there is not an existing message for the `Subject` in the process'
+/// mailbox or one does not arrive `within` the permitted timeout then the
+/// `Error(Nil)` is returned.
+///
+/// Only the process that is owner of the `Subject` can receive a message using
+/// it. If a process that does not own the `Subject` attempts to receive with it
+/// then it will not receive a message.
+///
+/// To wait for messages from multiple `Subject`s at the same time see the
+/// `Selector` type.
+///
+pub fn receive(
+ from subject: Subject(message),
+ within milliseconds: Int,
+) -> Result(message, Nil) {
+ new_selector()
+ |> selecting(subject, fn(x) { x })
+ |> select(within: milliseconds)
+}
+
+/// A type that enables a process to wait for messages from multiple `Subject`s
+/// at the same time, returning whichever message arrives first.
+///
+/// Used with the `new_selector`, `selecting`, and `select` functions.
+///
+/// # Examples
+///
+/// ```gleam
+/// > let int_subject = new_subject()
+/// > let float_subject = new_subject()
+/// > send(int_subject, 1)
+/// >
+/// > let selector =
+/// > new_selector()
+/// > |> selecting(int_subject, int.to_string)
+/// > |> selecting(float_subject, float.to_string)
+/// >
+/// > select(selector, 10)
+/// Ok("1")
+/// ```
+///
+pub type Selector(payload)
+
+/// Create a new `Selector` which can be used to receive messages on multiple
+/// `Subject`s at once.
+///
+@external(erlang, "gleam_erlang_ffi", "new_selector")
+pub fn new_selector() -> Selector(payload)
+
+/// Receive a message that has been sent to current process using any of the
+/// `Subject`s that have been added to the `Selector` with the `selecting`
+/// function.
+///
+/// If there is not an existing message for the `Selector` in the process'
+/// mailbox or one does not arrive `within` the permitted timeout then the
+/// `Error(Nil)` is returned.
+///
+/// Only the process that is owner of the `Subject`s can receive a message using
+/// them. If a process that does not own the a `Subject` attempts to receive
+/// with it then it will not receive a message.
+///
+/// To wait forever for the next message rather than for a limited amount of
+/// time see the `select_forever` function.
+///
+@external(erlang, "gleam_erlang_ffi", "select")
+pub fn select(
+ from from: Selector(payload),
+ within within: Int,
+) -> Result(payload, Nil)
+
+/// Similar to the `select` function but will wait forever for a message to
+/// arrive rather than timing out after a specified amount of time.
+///
+@external(erlang, "gleam_erlang_ffi", "select")
+pub fn select_forever(from from: Selector(payload)) -> payload
+
+/// Add a transformation function to a selector. When a message is received
+/// using this selector the transformation function is applied to the message.
+///
+/// This function can be used to change the type of messages received and may
+/// be useful when combined with the `merge_selector` function.
+///
+@external(erlang, "gleam_erlang_ffi", "map_selector")
+pub fn map_selector(a: Selector(a), b: fn(a) -> b) -> Selector(b)
+
+/// Merge one selector into another, producing a selector that contains the
+/// message handlers of both.
+///
+/// If a subject is handled by both selectors the handler function of the
+/// second selector is used.
+///
+@external(erlang, "gleam_erlang_ffi", "merge_selector")
+pub fn merge_selector(a: Selector(a), b: Selector(a)) -> Selector(a)
+
+pub type ExitMessage {
+ ExitMessage(pid: Pid, reason: ExitReason)
+}
+
+pub type ExitReason {
+ Normal
+ Killed
+ Abnormal(reason: String)
+}
+
+/// Add a handler for trapped exit messages. In order for these messages to be
+/// sent to the process when a linked process exits the process must call the
+/// `trap_exit` beforehand.
+///
+pub fn selecting_trapped_exits(
+ selector: Selector(a),
+ handler: fn(ExitMessage) -> a,
+) -> Selector(a) {
+ let tag = atom.create_from_string("EXIT")
+ let handler = fn(message: #(Atom, Pid, Dynamic)) -> a {
+ let reason = message.2
+ let normal = dynamic.from(Normal)
+ let killed = dynamic.from(Killed)
+ let reason = case dynamic.string(reason) {
+ _ if reason == normal -> Normal
+ _ if reason == killed -> Killed
+ Ok(reason) -> Abnormal(reason)
+ Error(_) -> Abnormal(string.inspect(reason))
+ }
+ handler(ExitMessage(message.1, reason))
+ }
+ insert_selector_handler(selector, #(tag, 3), handler)
+}
+
+// TODO: implement in Gleam
+/// Discard all messages in the current process' mailbox.
+///
+/// Warning: This function may cause other processes to crash if they sent a
+/// message to the current process and are waiting for a response, so use with
+/// caution.
+///
+@external(erlang, "gleam_erlang_ffi", "flush_messages")
+pub fn flush_messages() -> Nil
+
+/// Add a new `Subject` to the `Selector` to that it's messages can be received.
+///
+/// The `mapping` function provided with the `Subject` can be used to convert
+/// the type of messages received using this `Subject`. This is useful for when
+/// you wish to add multiple `Subject`s to a `Seletor` when they have differing
+/// message types. If you do not wish to transform the incoming messages in any
+/// way then the `identity` function can be given.
+///
+pub fn selecting(
+ selector: Selector(payload),
+ for subject: Subject(message),
+ mapping transform: fn(message) -> payload,
+) -> Selector(payload) {
+ let handler = fn(message: #(Reference, message)) { transform(message.1) }
+ insert_selector_handler(selector, #(subject.tag, 2), handler)
+}
+
+/// Add a handler to a selector for 2 element tuple messages with a given tag
+/// element in the first position.
+///
+/// Typically you want to use the `selecting` function with a `Subject` instead,
+/// but this function may be useful if you need to receive messages sent from
+/// other BEAM languages that do not use the `Subject` type.
+///
+pub fn selecting_record2(
+ selector: Selector(payload),
+ tag: tag,
+ mapping transform: fn(Dynamic) -> payload,
+) -> Selector(payload) {
+ let handler = fn(message: #(tag, Dynamic)) { transform(message.1) }
+ insert_selector_handler(selector, #(tag, 2), handler)
+}
+
+/// Add a handler to a selector for 3 element tuple messages with a given tag
+/// element in the first position.
+///
+/// Typically you want to use the `selecting` function with a `Subject` instead,
+/// but this function may be useful if you need to receive messages sent from
+/// other BEAM languages that do not use the `Subject` type.
+///
+pub fn selecting_record3(
+ selector: Selector(payload),
+ tag: tag,
+ mapping transform: fn(Dynamic, Dynamic) -> payload,
+) -> Selector(payload) {
+ let handler = fn(message: #(tag, Dynamic, Dynamic)) {
+ transform(message.1, message.2)
+ }
+ insert_selector_handler(selector, #(tag, 3), handler)
+}
+
+/// Add a handler to a selector for 4 element tuple messages with a given tag
+/// element in the first position.
+///
+/// Typically you want to use the `selecting` function with a `Subject` instead,
+/// but this function may be useful if you need to receive messages sent from
+/// other BEAM languages that do not use the `Subject` type.
+///
+pub fn selecting_record4(
+ selector: Selector(payload),
+ tag: tag,
+ mapping transform: fn(Dynamic, Dynamic, Dynamic) -> payload,
+) -> Selector(payload) {
+ let handler = fn(message: #(tag, Dynamic, Dynamic, Dynamic)) {
+ transform(message.1, message.2, message.3)
+ }
+ insert_selector_handler(selector, #(tag, 4), handler)
+}
+
+/// Add a handler to a selector for 5 element tuple messages with a given tag
+/// element in the first position.
+///
+/// Typically you want to use the `selecting` function with a `Subject` instead,
+/// but this function may be useful if you need to receive messages sent from
+/// other BEAM languages that do not use the `Subject` type.
+///
+pub fn selecting_record5(
+ selector: Selector(payload),
+ tag: tag,
+ mapping transform: fn(Dynamic, Dynamic, Dynamic, Dynamic) -> payload,
+) -> Selector(payload) {
+ let handler = fn(message: #(tag, Dynamic, Dynamic, Dynamic, Dynamic)) {
+ transform(message.1, message.2, message.3, message.4)
+ }
+ insert_selector_handler(selector, #(tag, 5), handler)
+}
+
+/// Add a handler to a selector for 6 element tuple messages with a given tag
+/// element in the first position.
+///
+/// Typically you want to use the `selecting` function with a `Subject` instead,
+/// but this function may be useful if you need to receive messages sent from
+/// other BEAM languages that do not use the `Subject` type.
+///
+pub fn selecting_record6(
+ selector: Selector(payload),
+ tag: tag,
+ mapping transform: fn(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic) -> payload,
+) -> Selector(payload) {
+ let handler = fn(message: #(tag, Dynamic, Dynamic, Dynamic, Dynamic, Dynamic)) {
+ transform(message.1, message.2, message.3, message.4, message.5)
+ }
+ insert_selector_handler(selector, #(tag, 6), handler)
+}
+
+/// Add a handler to a selector for 7 element tuple messages with a given tag
+/// element in the first position.
+///
+/// Typically you want to use the `selecting` function with a `Subject` instead,
+/// but this function may be useful if you need to receive messages sent from
+/// other BEAM languages that do not use the `Subject` type.
+///
+pub fn selecting_record7(
+ selector: Selector(payload),
+ tag: tag,
+ mapping transform: fn(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic, Dynamic) ->
+ payload,
+) -> Selector(payload) {
+ let handler = fn(
+ message: #(tag, Dynamic, Dynamic, Dynamic, Dynamic, Dynamic, Dynamic),
+ ) {
+ transform(message.1, message.2, message.3, message.4, message.5, message.6)
+ }
+ insert_selector_handler(selector, #(tag, 7), handler)
+}
+
+/// Add a handler to a selector for 8 element tuple messages with a given tag
+/// element in the first position.
+///
+/// Typically you want to use the `selecting` function with a `Subject` instead,
+/// but this function may be useful if you need to receive messages sent from
+/// other BEAM languages that do not use the `Subject` type.
+///
+pub fn selecting_record8(
+ selector: Selector(payload),
+ tag: tag,
+ mapping transform: fn(
+ Dynamic,
+ Dynamic,
+ Dynamic,
+ Dynamic,
+ Dynamic,
+ Dynamic,
+ Dynamic,
+ ) ->
+ payload,
+) -> Selector(payload) {
+ let handler = fn(
+ message: #(
+ tag,
+ Dynamic,
+ Dynamic,
+ Dynamic,
+ Dynamic,
+ Dynamic,
+ Dynamic,
+ Dynamic,
+ ),
+ ) {
+ transform(
+ message.1,
+ message.2,
+ message.3,
+ message.4,
+ message.5,
+ message.6,
+ message.7,
+ )
+ }
+ insert_selector_handler(selector, #(tag, 8), handler)
+}
+
+type AnythingSelectorTag {
+ Anything
+}
+
+/// Add a catch-all handler to a selector that will be used when no other
+/// handler in a selector is suitable for a given message.
+///
+/// This may be useful for when you want to ensure that any message in the inbox
+/// is handled, or when you need to handle messages from other BEAM languages
+/// which do not use subjects or record format messages.
+///
+pub fn selecting_anything(
+ selector: Selector(payload),
+ mapping handler: fn(Dynamic) -> payload,
+) -> Selector(payload) {
+ insert_selector_handler(selector, Anything, handler)
+}
+
+@external(erlang, "gleam_erlang_ffi", "insert_selector_handler")
+fn insert_selector_handler(
+ a: Selector(payload),
+ for for: tag,
+ mapping mapping: fn(message) -> payload,
+) -> Selector(payload)
+
+/// Suspends the process calling this function for the specified number of
+/// milliseconds.
+///
+@external(erlang, "gleam_erlang_ffi", "sleep")
+pub fn sleep(a: Int) -> Nil
+
+/// Suspends the process forever! This may be useful for suspending the main
+/// process in a Gleam program when it has no more work to do but we want other
+/// processes to continue to work.
+///
+@external(erlang, "gleam_erlang_ffi", "sleep_forever")
+pub fn sleep_forever() -> Nil
+
+/// Check to see whether the process for a given `Pid` is alive.
+///
+/// See the [Erlang documentation][1] for more information.
+///
+/// [1]: http://erlang.org/doc/man/erlang.html#is_process_alive-1
+///
+@external(erlang, "erlang", "is_process_alive")
+pub fn is_alive(a: Pid) -> Bool
+
+type ProcessMonitorFlag {
+ Process
+}
+
+@external(erlang, "erlang", "monitor")
+fn erlang_monitor_process(a: ProcessMonitorFlag, b: Pid) -> Reference
+
+pub opaque type ProcessMonitor {
+ ProcessMonitor(tag: Reference)
+}
+
+/// A message received when a monitored process exits.
+///
+pub type ProcessDown {
+ ProcessDown(pid: Pid, reason: Dynamic)
+}
+
+/// Start monitoring a process so that when the monitored process exits a
+/// message is sent to the monitoring process.
+///
+/// The message is only sent once, when the target process exits. If the
+/// process was not alive when this function is called the message will never
+/// be received.
+///
+/// The down message can be received with a `Selector` and the
+/// `selecting_process_down` function.
+///
+/// The process can be demonitored with the `demonitor_process` function.
+///
+pub fn monitor_process(pid: Pid) -> ProcessMonitor {
+ Process
+ |> erlang_monitor_process(pid)
+ |> ProcessMonitor
+}
+
+/// Add a `ProcessMonitor` to a `Selector` so that the `ProcessDown` message can
+/// be received using the `Selector` and the `select` function.
+///
+pub fn selecting_process_down(
+ selector: Selector(payload),
+ monitor: ProcessMonitor,
+ mapping: fn(ProcessDown) -> payload,
+) -> Selector(payload) {
+ insert_selector_handler(selector, monitor.tag, mapping)
+}
+
+/// Remove the monitor for a process so that when the monitor process exits a
+/// `ProcessDown` message is not sent to the monitoring process.
+///
+/// If the message has already been sent it is removed from the monitoring
+/// process' mailbox.
+///
+@external(erlang, "gleam_erlang_ffi", "demonitor")
+pub fn demonitor_process(monitor monitor: ProcessMonitor) -> Nil
+
+/// An error returned when making a call to a process.
+///
+pub type CallError(msg) {
+ /// The process being called exited before it sent a response.
+ ///
+ CalleeDown(reason: Dynamic)
+
+ /// The process being called did not response within the permitted amount of
+ /// time.
+ ///
+ CallTimeout
+}
+
+// This function is based off of Erlang's gen:do_call/4.
+/// Send a message to a process and wait for a reply.
+///
+/// If the receiving process exits or does not reply within the allowed amount
+/// of time then an error is returned.
+///
+pub fn try_call(
+ subject: Subject(request),
+ make_request: fn(Subject(response)) -> request,
+ within timeout: Int,
+) -> Result(response, CallError(response)) {
+ let reply_subject = new_subject()
+
+ // Monitor the callee process so we can tell if it goes down (meaning we
+ // won't get a reply)
+ let monitor = monitor_process(subject_owner(subject))
+
+ // Send the request to the process over the channel
+ send(subject, make_request(reply_subject))
+
+ // Await a reply or handle failure modes (timeout, process down, etc)
+ let result =
+ new_selector()
+ |> selecting(reply_subject, Ok)
+ |> selecting_process_down(
+ monitor,
+ fn(down: ProcessDown) { Error(CalleeDown(reason: down.reason)) },
+ )
+ |> select(timeout)
+
+ // Demonitor the process and close the channels as we're done
+ demonitor_process(monitor)
+
+ // Prepare an appropriate error (if present) for the caller
+ case result {
+ Error(Nil) -> Error(CallTimeout)
+ Ok(res) -> res
+ }
+}
+
+/// Send a message to a process and wait for a reply.
+///
+/// If the receiving process exits or does not reply within the allowed amount
+/// of time the calling process crashes. If you wish an error to be returned
+/// instead see the `try_call` function.
+///
+pub fn call(
+ subject: Subject(request),
+ make_request: fn(Subject(response)) -> request,
+ within timeout: Int,
+) -> response {
+ let assert Ok(resp) = try_call(subject, make_request, timeout)
+ resp
+}
+
+/// Creates a link between the calling process and another process.
+///
+/// When a process crashes any linked processes will also crash. This is useful
+/// to ensure that groups of processes that depend on each other all either
+/// succeed or fail together.
+///
+/// Returns `True` if the link was created successfully, returns `False` if the
+/// process was not alive and as such could not be linked.
+///
+@external(erlang, "gleam_erlang_ffi", "link")
+pub fn link(pid pid: Pid) -> Bool
+
+@external(erlang, "erlang", "unlink")
+fn erlang_unlink(pid pid: Pid) -> Bool
+
+/// Removes any existing link between the caller process and the target process.
+///
+pub fn unlink(pid: Pid) -> Nil {
+ erlang_unlink(pid)
+ Nil
+}
+
+pub type Timer
+
+@external(erlang, "erlang", "send_after")
+fn erlang_send_after(a: Int, b: Pid, c: msg) -> Timer
+
+/// Send a message over a channel after a specified number of milliseconds.
+///
+pub fn send_after(subject: Subject(msg), delay: Int, message: msg) -> Timer {
+ erlang_send_after(delay, subject.owner, #(subject.tag, message))
+}
+
+@external(erlang, "erlang", "cancel_timer")
+fn erlang_cancel_timer(a: Timer) -> Dynamic
+
+/// Values returned when a timer is cancelled.
+///
+pub type Cancelled {
+ /// The timer could not be found. It likely has already triggered.
+ ///
+ TimerNotFound
+
+ /// The timer was found and cancelled before it triggered.
+ ///
+ /// The amount of remaining time before the timer was due to be triggered is
+ /// returned in milliseconds.
+ ///
+ Cancelled(time_remaining: Int)
+}
+
+/// Cancel a given timer, causing it not to trigger if it has not done already.
+///
+pub fn cancel_timer(timer: Timer) -> Cancelled {
+ case dynamic.int(erlang_cancel_timer(timer)) {
+ Ok(i) -> Cancelled(i)
+ Error(_) -> TimerNotFound
+ }
+}
+
+type KillFlag {
+ Kill
+}
+
+@external(erlang, "erlang", "exit")
+fn erlang_kill(to to: Pid, because because: KillFlag) -> Bool
+
+// Go, my pretties. Kill! Kill!
+// - Bart Simpson
+//
+/// Send an untrappable `kill` exit signal to the target process.
+///
+/// See the documentation for the Erlang [`erlang:exit`][1] function for more
+/// information.
+///
+/// [1]: https://erlang.org/doc/man/erlang.html#exit-1
+///
+pub fn kill(pid: Pid) -> Nil {
+ erlang_kill(pid, Kill)
+ Nil
+}
+
+@external(erlang, "erlang", "exit")
+fn erlang_send_exit(to to: Pid, because because: whatever) -> Bool
+
+// TODO: test
+/// Sends an exit signal to a process, indicating that the process is to shut
+/// down.
+///
+/// See the [Erlang documentation][erl] for more information.
+/// [erl]: http://erlang.org/doc/man/erlang.html#exit-2
+///
+pub fn send_exit(to pid: Pid) -> Nil {
+ erlang_send_exit(pid, Normal)
+ Nil
+}
+
+/// Sends an exit signal to a process, indicating that the process is to shut
+/// down due to an abnormal reason such as a failure.
+///
+/// See the [Erlang documentation][erl] for more information.
+/// [erl]: http://erlang.org/doc/man/erlang.html#exit-2
+///
+pub fn send_abnormal_exit(pid: Pid, reason: String) -> Nil {
+ erlang_send_exit(pid, Abnormal(reason))
+ Nil
+}
+
+/// Set whether the current process is to trap exit signals or not.
+///
+/// When not trapping exits if a linked process crashes the exit signal
+/// propagates to the process which will also crash.
+/// This is the normal behaviour before this function is called.
+///
+/// When trapping exits (after this function is called) if a linked process
+/// crashes an exit message is sent to the process instead. These messages can
+/// be handled with the `selecting_trapped_exits` function.
+///
+@external(erlang, "gleam_erlang_ffi", "trap_exits")
+pub fn trap_exits(a: Bool) -> Nil
+
+/// Register a process under a given name, allowing it to be looked up using
+/// the `named` function.
+///
+/// This function will return an error under the following conditions:
+/// - The process for the pid no longer exists.
+/// - The name has already been registered.
+/// - The process already has a name.
+/// - The name is the atom `undefined`, which is reserved by Erlang.
+///
+@external(erlang, "gleam_erlang_ffi", "register_process")
+pub fn register(pid: Pid, name: Atom) -> Result(Nil, Nil)
+
+/// Un-register a process name, after which the process can no longer be looked
+/// up by that name, and both the name and the process can be re-used in other
+/// registrations.
+///
+/// It is possible to un-register process that are not from your application,
+/// including those from Erlang/OTP itself. This is not recommended and will
+/// likely result in undesirable behaviour and crashes.
+///
+@external(erlang, "gleam_erlang_ffi", "unregister_process")
+pub fn unregister(name: Atom) -> Result(Nil, Nil)
+
+/// Look up a process by name, returning the pid if it exists.
+///
+@external(erlang, "gleam_erlang_ffi", "process_named")
+pub fn named(name: Atom) -> Result(Pid, Nil)
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam@erlang.erl b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang.erl
new file mode 100644
index 0000000..14a5538
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang.erl
@@ -0,0 +1,90 @@
+-module(gleam@erlang).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([format/1, term_to_binary/1, get_line/1, system_time/1, erlang_timestamp/0, rescue/1, binary_to_term/1, unsafe_binary_to_term/1, start_arguments/0, ensure_all_started/1, make_reference/0, priv_directory/1]).
+-export_type([safe/0, get_line_error/0, time_unit/0, crash/0, ensure_all_started_error/0, reference_/0]).
+
+-type safe() :: safe.
+
+-type get_line_error() :: eof | no_data.
+
+-type time_unit() :: second | millisecond | microsecond | nanosecond.
+
+-type crash() :: {exited, gleam@dynamic:dynamic_()} |
+ {thrown, gleam@dynamic:dynamic_()} |
+ {errored, gleam@dynamic:dynamic_()}.
+
+-type ensure_all_started_error() :: {unknown_application,
+ gleam@erlang@atom:atom_()} |
+ {application_failed_to_start,
+ gleam@erlang@atom:atom_(),
+ gleam@dynamic:dynamic_()}.
+
+-type reference_() :: any().
+
+-spec format(any()) -> binary().
+format(Term) ->
+ unicode:characters_to_binary(io_lib:format(<<"~p"/utf8>>, [Term])).
+
+-spec term_to_binary(any()) -> bitstring().
+term_to_binary(A) ->
+ erlang:term_to_binary(A).
+
+-spec get_line(binary()) -> {ok, binary()} | {error, get_line_error()}.
+get_line(Prompt) ->
+ gleam_erlang_ffi:get_line(Prompt).
+
+-spec system_time(time_unit()) -> integer().
+system_time(A) ->
+ os:system_time(A).
+
+-spec erlang_timestamp() -> {integer(), integer(), integer()}.
+erlang_timestamp() ->
+ os:timestamp().
+
+-spec rescue(fun(() -> FHH)) -> {ok, FHH} | {error, crash()}.
+rescue(A) ->
+ gleam_erlang_ffi:rescue(A).
+
+-spec binary_to_term(bitstring()) -> {ok, gleam@dynamic:dynamic_()} |
+ {error, nil}.
+binary_to_term(Binary) ->
+ case gleam_erlang_ffi:rescue(
+ fun() -> erlang:binary_to_term(Binary, [safe]) end
+ ) of
+ {ok, Term} ->
+ {ok, Term};
+
+ {error, _} ->
+ {error, nil}
+ end.
+
+-spec unsafe_binary_to_term(bitstring()) -> {ok, gleam@dynamic:dynamic_()} |
+ {error, nil}.
+unsafe_binary_to_term(Binary) ->
+ case gleam_erlang_ffi:rescue(fun() -> erlang:binary_to_term(Binary, []) end) of
+ {ok, Term} ->
+ {ok, Term};
+
+ {error, _} ->
+ {error, nil}
+ end.
+
+-spec start_arguments() -> list(binary()).
+start_arguments() ->
+ _pipe = init:get_plain_arguments(),
+ gleam@list:map(_pipe, fun unicode:characters_to_binary/1).
+
+-spec ensure_all_started(gleam@erlang@atom:atom_()) -> {ok,
+ list(gleam@erlang@atom:atom_())} |
+ {error, ensure_all_started_error()}.
+ensure_all_started(Application) ->
+ gleam_erlang_ffi:ensure_all_started(Application).
+
+-spec make_reference() -> reference_().
+make_reference() ->
+ erlang:make_ref().
+
+-spec priv_directory(binary()) -> {ok, binary()} | {error, nil}.
+priv_directory(Name) ->
+ gleam_erlang_ffi:priv_directory(Name).
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@atom.erl b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@atom.erl
new file mode 100644
index 0000000..e9ad530
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@atom.erl
@@ -0,0 +1,26 @@
+-module(gleam@erlang@atom).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([from_string/1, create_from_string/1, to_string/1, from_dynamic/1]).
+-export_type([atom_/0, from_string_error/0]).
+
+-type atom_() :: any().
+
+-type from_string_error() :: atom_not_loaded.
+
+-spec from_string(binary()) -> {ok, atom_()} | {error, from_string_error()}.
+from_string(A) ->
+ gleam_erlang_ffi:atom_from_string(A).
+
+-spec create_from_string(binary()) -> atom_().
+create_from_string(A) ->
+ erlang:binary_to_atom(A).
+
+-spec to_string(atom_()) -> binary().
+to_string(A) ->
+ erlang:atom_to_binary(A).
+
+-spec from_dynamic(gleam@dynamic:dynamic_()) -> {ok, atom_()} |
+ {error, list(gleam@dynamic:decode_error())}.
+from_dynamic(From) ->
+ gleam_erlang_ffi:atom_from_dynamic(From).
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@charlist.erl b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@charlist.erl
new file mode 100644
index 0000000..9f9c0fa
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@charlist.erl
@@ -0,0 +1,15 @@
+-module(gleam@erlang@charlist).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([to_string/1, from_string/1]).
+-export_type([charlist/0]).
+
+-type charlist() :: any().
+
+-spec to_string(charlist()) -> binary().
+to_string(A) ->
+ unicode:characters_to_binary(A).
+
+-spec from_string(binary()) -> charlist().
+from_string(A) ->
+ unicode:characters_to_list(A).
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@file.erl b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@file.erl
new file mode 100644
index 0000000..1fe6628
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@file.erl
@@ -0,0 +1,190 @@
+-module(gleam@erlang@file).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([file_info/1, link_info/1, is_directory/1, is_regular/1, file_exists/1, link_exists/1, make_directory/1, list_directory/1, delete_directory/1, recursive_delete/1, read/1, read_bits/1, write/2, write_bits/2, append/2, append_bits/2, delete/1]).
+-export_type([reason/0, file_type/0, access/0, file_info/0]).
+
+-type reason() :: eacces |
+ eagain |
+ ebadf |
+ ebadmsg |
+ ebusy |
+ edeadlk |
+ edeadlock |
+ edquot |
+ eexist |
+ efault |
+ efbig |
+ eftype |
+ eintr |
+ einval |
+ eio |
+ eisdir |
+ eloop |
+ emfile |
+ emlink |
+ emultihop |
+ enametoolong |
+ enfile |
+ enobufs |
+ enodev |
+ enolck |
+ enolink |
+ enoent |
+ enomem |
+ enospc |
+ enosr |
+ enostr |
+ enosys |
+ enotblk |
+ enotdir |
+ enotsup |
+ enxio |
+ eopnotsupp |
+ eoverflow |
+ eperm |
+ epipe |
+ erange |
+ erofs |
+ espipe |
+ esrch |
+ estale |
+ etxtbsy |
+ exdev |
+ not_utf8.
+
+-type file_type() :: device | directory | other | regular | symlink.
+
+-type access() :: no_access | read | read_write | write.
+
+-type file_info() :: {file_info,
+ integer(),
+ file_type(),
+ access(),
+ integer(),
+ integer(),
+ integer(),
+ integer(),
+ integer(),
+ integer(),
+ integer(),
+ integer(),
+ integer(),
+ integer()}.
+
+-spec file_info(binary()) -> {ok, file_info()} | {error, reason()}.
+file_info(A) ->
+ gleam_erlang_ffi:file_info(A).
+
+-spec link_info(binary()) -> {ok, file_info()} | {error, reason()}.
+link_info(A) ->
+ gleam_erlang_ffi:link_info(A).
+
+-spec is_directory(binary()) -> {ok, boolean()} | {error, reason()}.
+is_directory(Path) ->
+ gleam@result:map(
+ gleam_erlang_ffi:file_info(Path),
+ fun(_use0) ->
+ {file_info, _, File_type, _, _, _, _, _, _, _, _, _, _, _} = _use0,
+ File_type =:= directory
+ end
+ ).
+
+-spec is_regular(binary()) -> {ok, boolean()} | {error, reason()}.
+is_regular(Path) ->
+ gleam@result:map(
+ gleam_erlang_ffi:file_info(Path),
+ fun(_use0) ->
+ {file_info, _, File_type, _, _, _, _, _, _, _, _, _, _, _} = _use0,
+ File_type =:= regular
+ end
+ ).
+
+-spec file_exists(binary()) -> {ok, boolean()} | {error, reason()}.
+file_exists(Path) ->
+ Result = begin
+ _pipe = Path,
+ _pipe@1 = gleam_erlang_ffi:file_info(_pipe),
+ gleam@result:replace(_pipe@1, true)
+ end,
+ case Result of
+ {error, enoent} ->
+ {ok, false};
+
+ _ ->
+ Result
+ end.
+
+-spec link_exists(binary()) -> {ok, boolean()} | {error, reason()}.
+link_exists(Path) ->
+ Result = begin
+ _pipe = Path,
+ _pipe@1 = gleam_erlang_ffi:link_info(_pipe),
+ gleam@result:replace(_pipe@1, true)
+ end,
+ case Result of
+ {error, enoent} ->
+ {ok, false};
+
+ _ ->
+ Result
+ end.
+
+-spec make_directory(binary()) -> {ok, nil} | {error, reason()}.
+make_directory(A) ->
+ gleam_erlang_ffi:make_directory(A).
+
+-spec list_directory(binary()) -> {ok, list(binary())} | {error, reason()}.
+list_directory(A) ->
+ gleam_erlang_ffi:list_directory(A).
+
+-spec delete_directory(binary()) -> {ok, nil} | {error, reason()}.
+delete_directory(A) ->
+ gleam_erlang_ffi:delete_directory(A).
+
+-spec recursive_delete(binary()) -> {ok, nil} | {error, reason()}.
+recursive_delete(A) ->
+ gleam_erlang_ffi:recursive_delete(A).
+
+-spec read(binary()) -> {ok, binary()} | {error, reason()}.
+read(Path) ->
+ _pipe = Path,
+ _pipe@1 = gleam_erlang_ffi:read_file(_pipe),
+ gleam@result:then(
+ _pipe@1,
+ fun(Content) -> case gleam@bit_array:to_string(Content) of
+ {ok, String} ->
+ {ok, String};
+
+ {error, nil} ->
+ {error, not_utf8}
+ end end
+ ).
+
+-spec read_bits(binary()) -> {ok, bitstring()} | {error, reason()}.
+read_bits(Path) ->
+ gleam_erlang_ffi:read_file(Path).
+
+-spec write(binary(), binary()) -> {ok, nil} | {error, reason()}.
+write(Contents, Path) ->
+ _pipe = Contents,
+ _pipe@1 = gleam_stdlib:identity(_pipe),
+ gleam_erlang_ffi:write_file(_pipe@1, Path).
+
+-spec write_bits(bitstring(), binary()) -> {ok, nil} | {error, reason()}.
+write_bits(Contents, Path) ->
+ gleam_erlang_ffi:write_file(Contents, Path).
+
+-spec append(binary(), binary()) -> {ok, nil} | {error, reason()}.
+append(Contents, Path) ->
+ _pipe = Contents,
+ _pipe@1 = gleam_stdlib:identity(_pipe),
+ gleam_erlang_ffi:append_file(_pipe@1, Path).
+
+-spec append_bits(bitstring(), binary()) -> {ok, nil} | {error, reason()}.
+append_bits(Contents, Path) ->
+ gleam_erlang_ffi:append_file(Contents, Path).
+
+-spec delete(binary()) -> {ok, nil} | {error, reason()}.
+delete(A) ->
+ gleam_erlang_ffi:delete_file(A).
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@node.erl b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@node.erl
new file mode 100644
index 0000000..f57d029
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@node.erl
@@ -0,0 +1,33 @@
+-module(gleam@erlang@node).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([self/0, visible/0, connect/1, send/3, to_atom/1]).
+-export_type([node_/0, do_not_leak/0, connect_error/0]).
+
+-type node_() :: any().
+
+-type do_not_leak() :: any().
+
+-type connect_error() :: failed_to_connect | local_node_is_not_alive.
+
+-spec self() -> node_().
+self() ->
+ erlang:node().
+
+-spec visible() -> list(node_()).
+visible() ->
+ erlang:nodes().
+
+-spec connect(gleam@erlang@atom:atom_()) -> {ok, node_()} |
+ {error, connect_error()}.
+connect(Node) ->
+ gleam_erlang_ffi:connect_node(Node).
+
+-spec send(node_(), gleam@erlang@atom:atom_(), any()) -> nil.
+send(Node, Name, Message) ->
+ erlang:send({Name, Node}, Message),
+ nil.
+
+-spec to_atom(node_()) -> gleam@erlang@atom:atom_().
+to_atom(Node) ->
+ gleam_erlang_ffi:identity(Node).
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@os.erl b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@os.erl
new file mode 100644
index 0000000..6604255
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@os.erl
@@ -0,0 +1,27 @@
+-module(gleam@erlang@os).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([get_all_env/0, get_env/1, set_env/2, unset_env/1, family/0]).
+-export_type([os_family/0]).
+
+-type os_family() :: windows_nt | linux | darwin | free_bsd | {other, binary()}.
+
+-spec get_all_env() -> gleam@map:map_(binary(), binary()).
+get_all_env() ->
+ gleam_erlang_ffi:get_all_env().
+
+-spec get_env(binary()) -> {ok, binary()} | {error, nil}.
+get_env(Name) ->
+ gleam_erlang_ffi:get_env(Name).
+
+-spec set_env(binary(), binary()) -> nil.
+set_env(Name, Value) ->
+ gleam_erlang_ffi:set_env(Name, Value).
+
+-spec unset_env(binary()) -> nil.
+unset_env(Name) ->
+ gleam_erlang_ffi:unset_env(Name).
+
+-spec family() -> os_family().
+family() ->
+ gleam_erlang_ffi:os_family().
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@process.erl b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@process.erl
new file mode 100644
index 0000000..fc8e0ff
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam@erlang@process.erl
@@ -0,0 +1,374 @@
+-module(gleam@erlang@process).
+-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
+
+-export([self/0, start/2, new_subject/0, subject_owner/1, send/2, new_selector/0, select/2, select_forever/1, map_selector/2, merge_selector/2, flush_messages/0, selecting_trapped_exits/2, selecting/3, 'receive'/2, selecting_record2/3, selecting_record3/3, selecting_record4/3, selecting_record5/3, selecting_record6/3, selecting_record7/3, selecting_record8/3, selecting_anything/2, sleep/1, sleep_forever/0, is_alive/1, monitor_process/1, selecting_process_down/3, demonitor_process/1, try_call/3, call/3, link/1, unlink/1, send_after/3, cancel_timer/1, kill/1, send_exit/1, send_abnormal_exit/2, trap_exits/1, register/2, unregister/1, named/1]).
+-export_type([pid_/0, subject/1, do_not_leak/0, selector/1, exit_message/0, exit_reason/0, anything_selector_tag/0, process_monitor_flag/0, process_monitor/0, process_down/0, call_error/1, timer/0, cancelled/0, kill_flag/0]).
+
+-type pid_() :: any().
+
+-opaque subject(FJD) :: {subject, pid_(), gleam@erlang:reference_()} |
+ {gleam_phantom, FJD}.
+
+-type do_not_leak() :: any().
+
+-type selector(FJE) :: any() | {gleam_phantom, FJE}.
+
+-type exit_message() :: {exit_message, pid_(), exit_reason()}.
+
+-type exit_reason() :: normal | killed | {abnormal, binary()}.
+
+-type anything_selector_tag() :: anything.
+
+-type process_monitor_flag() :: process.
+
+-opaque process_monitor() :: {process_monitor, gleam@erlang:reference_()}.
+
+-type process_down() :: {process_down, pid_(), gleam@dynamic:dynamic_()}.
+
+-type call_error(FJF) :: {callee_down, gleam@dynamic:dynamic_()} |
+ call_timeout |
+ {gleam_phantom, FJF}.
+
+-type timer() :: any().
+
+-type cancelled() :: timer_not_found | {cancelled, integer()}.
+
+-type kill_flag() :: kill.
+
+-spec self() -> pid_().
+self() ->
+ erlang:self().
+
+-spec start(fun(() -> any()), boolean()) -> pid_().
+start(Implementation, Link) ->
+ case Link of
+ true ->
+ erlang:spawn_link(Implementation);
+
+ false ->
+ erlang:spawn(Implementation)
+ end.
+
+-spec new_subject() -> subject(any()).
+new_subject() ->
+ {subject, erlang:self(), erlang:make_ref()}.
+
+-spec subject_owner(subject(any())) -> pid_().
+subject_owner(Subject) ->
+ erlang:element(2, Subject).
+
+-spec send(subject(FJO), FJO) -> nil.
+send(Subject, Message) ->
+ erlang:send(
+ erlang:element(2, Subject),
+ {erlang:element(3, Subject), Message}
+ ),
+ nil.
+
+-spec new_selector() -> selector(any()).
+new_selector() ->
+ gleam_erlang_ffi:new_selector().
+
+-spec select(selector(FJW), integer()) -> {ok, FJW} | {error, nil}.
+select(From, Within) ->
+ gleam_erlang_ffi:select(From, Within).
+
+-spec select_forever(selector(FKA)) -> FKA.
+select_forever(From) ->
+ gleam_erlang_ffi:select(From).
+
+-spec map_selector(selector(FKC), fun((FKC) -> FKE)) -> selector(FKE).
+map_selector(A, B) ->
+ gleam_erlang_ffi:map_selector(A, B).
+
+-spec merge_selector(selector(FKG), selector(FKG)) -> selector(FKG).
+merge_selector(A, B) ->
+ gleam_erlang_ffi:merge_selector(A, B).
+
+-spec flush_messages() -> nil.
+flush_messages() ->
+ gleam_erlang_ffi:flush_messages().
+
+-spec selecting_trapped_exits(selector(FKK), fun((exit_message()) -> FKK)) -> selector(FKK).
+selecting_trapped_exits(Selector, Handler) ->
+ Tag = erlang:binary_to_atom(<<"EXIT"/utf8>>),
+ Handler@1 = fun(Message) ->
+ Reason = erlang:element(3, Message),
+ Normal = gleam@dynamic:from(normal),
+ Killed = gleam@dynamic:from(killed),
+ Reason@2 = case gleam@dynamic:string(Reason) of
+ _ when Reason =:= Normal ->
+ normal;
+
+ _ when Reason =:= Killed ->
+ killed;
+
+ {ok, Reason@1} ->
+ {abnormal, Reason@1};
+
+ {error, _} ->
+ {abnormal, gleam@string:inspect(Reason)}
+ end,
+ Handler({exit_message, erlang:element(2, Message), Reason@2})
+ end,
+ gleam_erlang_ffi:insert_selector_handler(Selector, {Tag, 3}, Handler@1).
+
+-spec selecting(selector(FKN), subject(FKP), fun((FKP) -> FKN)) -> selector(FKN).
+selecting(Selector, Subject, Transform) ->
+ Handler = fun(Message) -> Transform(erlang:element(2, Message)) end,
+ gleam_erlang_ffi:insert_selector_handler(
+ Selector,
+ {erlang:element(3, Subject), 2},
+ Handler
+ ).
+
+-spec 'receive'(subject(FJQ), integer()) -> {ok, FJQ} | {error, nil}.
+'receive'(Subject, Milliseconds) ->
+ _pipe = gleam_erlang_ffi:new_selector(),
+ _pipe@1 = selecting(_pipe, Subject, fun(X) -> X end),
+ gleam_erlang_ffi:select(_pipe@1, Milliseconds).
+
+-spec selecting_record2(
+ selector(FKS),
+ any(),
+ fun((gleam@dynamic:dynamic_()) -> FKS)
+) -> selector(FKS).
+selecting_record2(Selector, Tag, Transform) ->
+ Handler = fun(Message) -> Transform(erlang:element(2, Message)) end,
+ gleam_erlang_ffi:insert_selector_handler(Selector, {Tag, 2}, Handler).
+
+-spec selecting_record3(
+ selector(FKW),
+ any(),
+ fun((gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_()) -> FKW)
+) -> selector(FKW).
+selecting_record3(Selector, Tag, Transform) ->
+ Handler = fun(Message) ->
+ Transform(erlang:element(2, Message), erlang:element(3, Message))
+ end,
+ gleam_erlang_ffi:insert_selector_handler(Selector, {Tag, 3}, Handler).
+
+-spec selecting_record4(
+ selector(FLA),
+ any(),
+ fun((gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_()) -> FLA)
+) -> selector(FLA).
+selecting_record4(Selector, Tag, Transform) ->
+ Handler = fun(Message) ->
+ Transform(
+ erlang:element(2, Message),
+ erlang:element(3, Message),
+ erlang:element(4, Message)
+ )
+ end,
+ gleam_erlang_ffi:insert_selector_handler(Selector, {Tag, 4}, Handler).
+
+-spec selecting_record5(
+ selector(FLE),
+ any(),
+ fun((gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_()) -> FLE)
+) -> selector(FLE).
+selecting_record5(Selector, Tag, Transform) ->
+ Handler = fun(Message) ->
+ Transform(
+ erlang:element(2, Message),
+ erlang:element(3, Message),
+ erlang:element(4, Message),
+ erlang:element(5, Message)
+ )
+ end,
+ gleam_erlang_ffi:insert_selector_handler(Selector, {Tag, 5}, Handler).
+
+-spec selecting_record6(
+ selector(FLI),
+ any(),
+ fun((gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_()) -> FLI)
+) -> selector(FLI).
+selecting_record6(Selector, Tag, Transform) ->
+ Handler = fun(Message) ->
+ Transform(
+ erlang:element(2, Message),
+ erlang:element(3, Message),
+ erlang:element(4, Message),
+ erlang:element(5, Message),
+ erlang:element(6, Message)
+ )
+ end,
+ gleam_erlang_ffi:insert_selector_handler(Selector, {Tag, 6}, Handler).
+
+-spec selecting_record7(
+ selector(FLM),
+ any(),
+ fun((gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_()) -> FLM)
+) -> selector(FLM).
+selecting_record7(Selector, Tag, Transform) ->
+ Handler = fun(Message) ->
+ Transform(
+ erlang:element(2, Message),
+ erlang:element(3, Message),
+ erlang:element(4, Message),
+ erlang:element(5, Message),
+ erlang:element(6, Message),
+ erlang:element(7, Message)
+ )
+ end,
+ gleam_erlang_ffi:insert_selector_handler(Selector, {Tag, 7}, Handler).
+
+-spec selecting_record8(
+ selector(FLQ),
+ any(),
+ fun((gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_(), gleam@dynamic:dynamic_()) -> FLQ)
+) -> selector(FLQ).
+selecting_record8(Selector, Tag, Transform) ->
+ Handler = fun(Message) ->
+ Transform(
+ erlang:element(2, Message),
+ erlang:element(3, Message),
+ erlang:element(4, Message),
+ erlang:element(5, Message),
+ erlang:element(6, Message),
+ erlang:element(7, Message),
+ erlang:element(8, Message)
+ )
+ end,
+ gleam_erlang_ffi:insert_selector_handler(Selector, {Tag, 8}, Handler).
+
+-spec selecting_anything(selector(FLU), fun((gleam@dynamic:dynamic_()) -> FLU)) -> selector(FLU).
+selecting_anything(Selector, Handler) ->
+ gleam_erlang_ffi:insert_selector_handler(Selector, anything, Handler).
+
+-spec sleep(integer()) -> nil.
+sleep(A) ->
+ gleam_erlang_ffi:sleep(A).
+
+-spec sleep_forever() -> nil.
+sleep_forever() ->
+ gleam_erlang_ffi:sleep_forever().
+
+-spec is_alive(pid_()) -> boolean().
+is_alive(A) ->
+ erlang:is_process_alive(A).
+
+-spec monitor_process(pid_()) -> process_monitor().
+monitor_process(Pid) ->
+ _pipe = process,
+ _pipe@1 = erlang:monitor(_pipe, Pid),
+ {process_monitor, _pipe@1}.
+
+-spec selecting_process_down(
+ selector(FMC),
+ process_monitor(),
+ fun((process_down()) -> FMC)
+) -> selector(FMC).
+selecting_process_down(Selector, Monitor, Mapping) ->
+ gleam_erlang_ffi:insert_selector_handler(
+ Selector,
+ erlang:element(2, Monitor),
+ Mapping
+ ).
+
+-spec demonitor_process(process_monitor()) -> nil.
+demonitor_process(Monitor) ->
+ gleam_erlang_ffi:demonitor(Monitor).
+
+-spec try_call(subject(FMF), fun((subject(FMH)) -> FMF), integer()) -> {ok, FMH} |
+ {error, call_error(FMH)}.
+try_call(Subject, Make_request, Timeout) ->
+ Reply_subject = new_subject(),
+ Monitor = monitor_process(subject_owner(Subject)),
+ send(Subject, Make_request(Reply_subject)),
+ Result = begin
+ _pipe = gleam_erlang_ffi:new_selector(),
+ _pipe@1 = selecting(
+ _pipe,
+ Reply_subject,
+ fun(Field@0) -> {ok, Field@0} end
+ ),
+ _pipe@2 = selecting_process_down(
+ _pipe@1,
+ Monitor,
+ fun(Down) -> {error, {callee_down, erlang:element(3, Down)}} end
+ ),
+ gleam_erlang_ffi:select(_pipe@2, Timeout)
+ end,
+ gleam_erlang_ffi:demonitor(Monitor),
+ case Result of
+ {error, nil} ->
+ {error, call_timeout};
+
+ {ok, Res} ->
+ Res
+ end.
+
+-spec call(subject(FMM), fun((subject(FMO)) -> FMM), integer()) -> FMO.
+call(Subject, Make_request, Timeout) ->
+ _assert_subject = try_call(Subject, Make_request, Timeout),
+ {ok, Resp} = case _assert_subject of
+ {ok, _} -> _assert_subject;
+ _assert_fail ->
+ erlang:error(#{gleam_error => let_assert,
+ message => <<"Assertion pattern match failed"/utf8>>,
+ value => _assert_fail,
+ module => <<"gleam/erlang/process"/utf8>>,
+ function => <<"call"/utf8>>,
+ line => 593})
+ end,
+ Resp.
+
+-spec link(pid_()) -> boolean().
+link(Pid) ->
+ gleam_erlang_ffi:link(Pid).
+
+-spec unlink(pid_()) -> nil.
+unlink(Pid) ->
+ erlang:unlink(Pid),
+ nil.
+
+-spec send_after(subject(FMR), integer(), FMR) -> timer().
+send_after(Subject, Delay, Message) ->
+ erlang:send_after(
+ Delay,
+ erlang:element(2, Subject),
+ {erlang:element(3, Subject), Message}
+ ).
+
+-spec cancel_timer(timer()) -> cancelled().
+cancel_timer(Timer) ->
+ case gleam@dynamic:int(erlang:cancel_timer(Timer)) of
+ {ok, I} ->
+ {cancelled, I};
+
+ {error, _} ->
+ timer_not_found
+ end.
+
+-spec kill(pid_()) -> nil.
+kill(Pid) ->
+ erlang:exit(Pid, kill),
+ nil.
+
+-spec send_exit(pid_()) -> nil.
+send_exit(Pid) ->
+ erlang:exit(Pid, normal),
+ nil.
+
+-spec send_abnormal_exit(pid_(), binary()) -> nil.
+send_abnormal_exit(Pid, Reason) ->
+ erlang:exit(Pid, {abnormal, Reason}),
+ nil.
+
+-spec trap_exits(boolean()) -> nil.
+trap_exits(A) ->
+ gleam_erlang_ffi:trap_exits(A).
+
+-spec register(pid_(), gleam@erlang@atom:atom_()) -> {ok, nil} | {error, nil}.
+register(Pid, Name) ->
+ gleam_erlang_ffi:register_process(Pid, Name).
+
+-spec unregister(gleam@erlang@atom:atom_()) -> {ok, nil} | {error, nil}.
+unregister(Name) ->
+ gleam_erlang_ffi:unregister_process(Name).
+
+-spec named(gleam@erlang@atom:atom_()) -> {ok, pid_()} | {error, nil}.
+named(Name) ->
+ gleam_erlang_ffi:process_named(Name).
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam_erlang.app.src b/aoc2023/build/packages/gleam_erlang/src/gleam_erlang.app.src
new file mode 100644
index 0000000..bb1b8e6
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam_erlang.app.src
@@ -0,0 +1,14 @@
+{application, gleam_erlang, [
+ {vsn, "0.23.1"},
+ {applications, [gleam_stdlib,
+ gleeunit]},
+ {description, "A Gleam library for working with Erlang"},
+ {modules, [gleam@erlang,
+ gleam@erlang@atom,
+ gleam@erlang@charlist,
+ gleam@erlang@file,
+ gleam@erlang@node,
+ gleam@erlang@os,
+ gleam@erlang@process]},
+ {registered, []}
+]}.
diff --git a/aoc2023/build/packages/gleam_erlang/src/gleam_erlang_ffi.erl b/aoc2023/build/packages/gleam_erlang/src/gleam_erlang_ffi.erl
new file mode 100644
index 0000000..872126f
--- /dev/null
+++ b/aoc2023/build/packages/gleam_erlang/src/gleam_erlang_ffi.erl
@@ -0,0 +1,263 @@
+-module(gleam_erlang_ffi).
+-export([
+ atom_from_dynamic/1, rescue/1, atom_from_string/1, get_line/1,
+ ensure_all_started/1, sleep/1, os_family/0, sleep_forever/0, read_file/1,
+ append_file/2, write_file/2, delete_file/1, get_all_env/0, get_env/1,
+ set_env/2, unset_env/1, delete_directory/1, recursive_delete/1,
+ list_directory/1, demonitor/1, make_directory/1, new_selector/0, link/1,
+ insert_selector_handler/3, select/1, select/2, trap_exits/1, map_selector/2,
+ merge_selector/2, flush_messages/0, file_info/1, link_info/1,
+ priv_directory/1, connect_node/1, register_process/2, unregister_process/1,
+ process_named/1, identity/1
+]).
+
+-define(is_posix_error(Error),
+ Error =:= eacces orelse Error =:= eagain orelse Error =:= ebadf orelse
+ Error =:= ebadmsg orelse Error =:= ebusy orelse Error =:= edeadlk orelse
+ Error =:= edeadlock orelse Error =:= edquot orelse Error =:= eexist orelse
+ Error =:= efault orelse Error =:= efbig orelse Error =:= eftype orelse
+ Error =:= eintr orelse Error =:= einval orelse Error =:= eio orelse
+ Error =:= eisdir orelse Error =:= eloop orelse Error =:= emfile orelse
+ Error =:= emlink orelse Error =:= emultihop orelse Error =:= enametoolong orelse
+ Error =:= enfile orelse Error =:= enobufs orelse Error =:= enodev orelse
+ Error =:= enolck orelse Error =:= enolink orelse Error =:= enoent orelse
+ Error =:= enomem orelse Error =:= enospc orelse Error =:= enosr orelse
+ Error =:= enostr orelse Error =:= enosys orelse Error =:= enotblk orelse
+ Error =:= enotdir orelse Error =:= enotsup orelse Error =:= enxio orelse
+ Error =:= eopnotsupp orelse Error =:= eoverflow orelse Error =:= eperm orelse
+ Error =:= epipe orelse Error =:= erange orelse Error =:= erofs orelse
+ Error =:= espipe orelse Error =:= esrch orelse Error =:= estale orelse
+ Error =:= etxtbsy orelse Error =:= exdev
+).
+
+-spec atom_from_string(binary()) -> {ok, atom()} | {error, atom_not_loaded}.
+atom_from_string(S) ->
+ try {ok, binary_to_existing_atom(S)}
+ catch error:badarg -> {error, atom_not_loaded}
+ end.
+
+atom_from_dynamic(Data) when is_atom(Data) ->
+ {ok, Data};
+atom_from_dynamic(Data) ->
+ {error, [{decode_error, <<"Atom">>, gleam@dynamic:classify(Data), []}]}.
+
+-spec get_line(io:prompt()) -> {ok, unicode:unicode_binary()} | {error, eof | no_data}.
+get_line(Prompt) ->
+ case io:get_line(Prompt) of
+ eof -> {error, eof};
+ {error, _} -> {error, no_data};
+ Data when is_binary(Data) -> {ok, Data};
+ Data when is_list(Data) -> {ok, unicode:characters_to_binary(Data)}
+ end.
+
+rescue(F) ->
+ try {ok, F()}
+ catch
+ throw:X -> {error, {thrown, X}};
+ error:X -> {error, {errored, X}};
+ exit:X -> {error, {exited, X}}
+ end.
+
+ensure_all_started(Application) ->
+ case application:ensure_all_started(Application) of
+ {ok, _} = Ok -> Ok;
+
+ {error, {ProblemApp, {"no such file or directory", _}}} ->
+ {error, {unknown_application, ProblemApp}}
+ end.
+
+sleep(Microseconds) ->
+ timer:sleep(Microseconds),
+ nil.
+
+sleep_forever() ->
+ timer:sleep(infinity),
+ nil.
+
+file_info_result(Result) ->
+ case Result of
+ {ok, {file_info, Size, Type, Access, Atime, Mtime, Ctime, Mode, Links, MajorDevice, MinorDevice, Inode, Uid, Gid}} when Access =:= none ->
+ {ok, {file_info, Size, Type, no_access, Atime, Mtime, Ctime, Mode, Links, MajorDevice, MinorDevice, Inode, Uid, Gid}};
+ {ok, _} ->
+ Result;
+ {error, Reason} when ?is_posix_error(Reason) ->
+ Result
+ end.
+
+file_info(Filename) ->
+ file_info_result(file:read_file_info(Filename, [{time, posix}])).
+
+link_info(Filename) ->
+ file_info_result(file:read_link_info(Filename, [{time, posix}])).
+
+posix_result(Result) ->
+ case Result of
+ ok -> {ok, nil};
+ {ok, Value} -> {ok, Value};
+ {error, Reason} when ?is_posix_error(Reason) -> {error, Reason}
+ end.
+
+read_file(Filename) ->
+ posix_result(file:read_file(Filename)).
+
+write_file(Contents, Filename) ->
+ posix_result(file:write_file(Filename, Contents)).
+
+append_file(Contents, Filename) ->
+ posix_result(file:write_file(Filename, Contents, [append])).
+
+delete_file(Filename) ->
+ posix_result(file:delete(Filename)).
+
+make_directory(Dir) ->
+ posix_result(file:make_dir(Dir)).
+
+list_directory(Dir) ->
+ case file:list_dir(Dir) of
+ {ok, Filenames} ->
+ {ok, [list_to_binary(Filename) || Filename <- Filenames]};
+ {error, Reason} when ?is_posix_error(Reason) ->
+ {error, Reason}
+ end.
+
+delete_directory(Dir) ->
+ posix_result(file:del_dir(Dir)).
+
+recursive_delete(Dir) ->
+ posix_result(file:del_dir_r(Dir)).
+
+get_all_env() ->
+ BinVars = lists:map(fun(VarString) ->
+ [VarName, VarVal] = string:split(VarString, "="),
+ {list_to_binary(VarName), list_to_binary(VarVal)}
+ end, os:getenv()),
+ maps:from_list(BinVars).
+
+get_env(Name) ->
+ case os:getenv(binary_to_list(Name)) of
+ false -> {error, nil};
+ Value -> {ok, list_to_binary(Value)}
+ end.
+
+set_env(Name, Value) ->
+ os:putenv(binary_to_list(Name), binary_to_list(Value)),
+ nil.
+
+unset_env(Name) ->
+ os:unsetenv(binary_to_list(Name)),
+ nil.
+
+os_family() ->
+ case os:type() of
+ {win32, nt} ->
+ windows_nt;
+ {unix, linux} ->
+ linux;
+ {unix, darwin} ->
+ darwin;
+ {unix, freebsd} ->
+ free_bsd;
+ {_, Other} ->
+ {other, atom_to_binary(Other, utf8)}
+ end.
+
+new_selector() ->
+ {selector, #{}}.
+
+map_selector({selector, Handlers}, Fn) ->
+ MappedHandlers = maps:map(fun(_Tag, Handler) ->
+ fun(Message) -> Fn(Handler(Message)) end
+ end, Handlers),
+ {selector, MappedHandlers}.
+
+merge_selector({selector, HandlersA}, {selector, HandlersB}) ->
+ {selector, maps:merge(HandlersA, HandlersB)}.
+
+insert_selector_handler({selector, Handlers}, Tag, Fn) ->
+ {selector, Handlers#{Tag => Fn}}.
+
+select(Selector) ->
+ {ok, Message} = select(Selector, infinity),
+ Message.
+
+select({selector, Handlers}, Timeout) ->
+ AnythingHandler = maps:get(anything, Handlers, undefined),
+ receive
+ % Monitored process down messages.
+ % This is special cased so we can selectively receive based on the
+ % reference as well as the record tag.
+ {'DOWN', Ref, process, Pid, Reason} when is_map_key(Ref, Handlers) ->
+ Fn = maps:get(Ref, Handlers),
+ {ok, Fn({process_down, Pid, Reason})};
+
+ Msg when is_map_key({element(1, Msg), tuple_size(Msg)}, Handlers) ->
+ Fn = maps:get({element(1, Msg), tuple_size(Msg)}, Handlers),
+ {ok, Fn(Msg)};
+
+ Msg when AnythingHandler =/= undefined ->
+ {ok, AnythingHandler(Msg)}
+ after Timeout ->
+ {error, nil}
+ end.
+
+demonitor({_, Reference}) ->
+ erlang:demonitor(Reference, [flush]).
+
+link(Pid) ->
+ try
+ erlang:link(Pid)
+ catch
+ error:_ -> false
+ end.
+
+trap_exits(ShouldTrap) ->
+ erlang:process_flag(trap_exit, ShouldTrap),
+ nil.
+
+flush_messages() ->
+ receive _Message -> flush_messages()
+ after 0 -> nil
+ end.
+
+priv_directory(Name) ->
+ try erlang:binary_to_existing_atom(Name) of
+ Atom ->
+ case code:priv_dir(Atom) of
+ {error, _} -> {error, nil};
+ Path -> {ok, unicode:characters_to_binary(Path)}
+ end
+ catch
+ error:badarg -> {error, nil}
+ end.
+
+connect_node(Node) ->
+ case net_kernel:connect_node(Node) of
+ true -> {ok, Node};
+ false -> {error, failed_to_connect};
+ ignored -> {error, local_node_is_not_alive}
+ end.
+
+register_process(Pid, Name) ->
+ try
+ true = erlang:register(Name, Pid),
+ {ok, nil}
+ catch
+ error:badarg -> {error, nil}
+ end.
+
+unregister_process(Name) ->
+ try
+ true = erlang:unregister(Name),
+ {ok, nil}
+ catch
+ error:badarg -> {error, nil}
+ end.
+
+process_named(Name) ->
+ case erlang:whereis(Name) of
+ Pid when is_pid(Pid) -> {ok, Pid};
+ _ -> {error, nil}
+ end.
+
+identity(X) ->
+ X.