%
% This file is part of AtomVM.
%
% Copyright 2018 Fred Dushin <fred@dushin.net>
%
% 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.
%
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
%

%%-----------------------------------------------------------------------------
%% @doc An implementation of the Erlang/OTP erlang module, for functions
%% that are not already defined as NIFs.
%% @end
%%-----------------------------------------------------------------------------
-module(erlang).

-export([
    apply/2,
    apply/3,
    start_timer/3, start_timer/4,
    cancel_timer/1,
    send_after/3,
    process_info/2,
    system_info/1,
    system_flag/2,
    md5/1,
    is_map/1,
    is_map_key/2,
    is_record/2,
    map_size/1,
    map_get/2,
    monotonic_time/0,
    monotonic_time/1,
    min/2,
    max/2,
    memory/1,
    nif_error/1,
    get/0,
    get/1,
    put/2,
    erase/0,
    erase/1,
    function_exported/3,
    loaded/0,
    module_loaded/1,
    display/1,
    list_to_atom/1,
    list_to_existing_atom/1,
    list_to_binary/1,
    list_to_integer/1,
    list_to_integer/2,
    list_to_tuple/1,
    iolist_size/1,
    iolist_to_binary/1,
    binary_to_atom/1,
    binary_to_atom/2,
    binary_to_integer/1,
    binary_to_integer/2,
    binary_to_list/1,
    atom_to_binary/1,
    atom_to_binary/2,
    atom_to_list/1,
    float_to_binary/1,
    float_to_binary/2,
    float_to_list/1,
    float_to_list/2,
    fun_info/1,
    fun_info/2,
    integer_to_binary/1,
    integer_to_binary/2,
    integer_to_list/1,
    integer_to_list/2,
    fun_to_list/1,
    pid_to_list/1,
    port_to_list/1,
    ref_to_list/1,
    register/2,
    unregister/1,
    whereis/1,
    spawn/1,
    spawn/3,
    spawn_link/1,
    spawn_link/3,
    spawn_monitor/1,
    spawn_monitor/3,
    spawn_opt/2,
    spawn_opt/4,
    link/1,
    unlink/1,
    make_ref/0,
    send/2,
    monitor/2,
    demonitor/1,
    demonitor/2,
    exit/1,
    exit/2,
    open_port/2,
    system_time/0,
    system_time/1,
    group_leader/0,
    group_leader/2,
    process_flag/2,
    get_module_info/1,
    get_module_info/2,
    processes/0,
    is_process_alive/1,
    garbage_collect/0,
    garbage_collect/1,
    binary_to_term/1,
    term_to_binary/1,
    split_binary/2,
    crc32/1,
    crc32/2,
    crc32_combine/3,
    timestamp/0,
    universaltime/0,
    localtime/0,
    localtime/1,
    setnode/2,
    setnode/3,
    is_alive/0,
    get_cookie/0,
    get_cookie/1,
    set_cookie/1,
    set_cookie/2,
    dist_ctrl_get_data_notification/1,
    dist_ctrl_get_data/1,
    dist_ctrl_put_data/2,
    unique_integer/0,
    unique_integer/1,
    raise/3,
    abs/1,
    byte_size/1,
    element/2,
    error/1,
    error/2,
    hd/1,
    is_atom/1,
    is_binary/1,
    is_boolean/1,
    is_float/1,
    is_function/1,
    is_function/2,
    is_integer/1,
    is_integer/3,
    is_list/1,
    is_number/1,
    is_pid/1,
    is_reference/1,
    is_tuple/1,
    length/1,
    list_to_float/1,
    node/0,
    round/1,
    self/0,
    setelement/3,
    size/1,
    throw/1,
    tl/1,
    trunc/1,
    tuple_size/1,
    tuple_to_list/1
]).

-export_type([
    time_unit/0,
    timestamp/0,
    atomvm_heap_growth_strategy/0,
    stacktrace/0,
    stacktrace_extrainfo/0,
    raise_stacktrace/0
]).

%%
%% TODO Correct the following bugs
%% * cancel_timer should be renamed cancel, per the OTP documentation
%% * return value needs to be {ok, cancel} or {error, Reason}
%% * review API documentation for timer functions in this module
%%

-type atom_encoding() :: latin1 | utf8 | unicode.

-type mem_type() :: binary.
-type time_unit() :: second | millisecond | microsecond | nanosecond | native.
-type timestamp() :: {
    MegaSecs :: non_neg_integer(), Secs :: non_neg_integer(), MicroSecs :: non_neg_integer
}.

-type float_format_option() ::
    {decimals, Decimals :: 0..57}
    | {scientific, Decimals :: 0..57}
    | compact.

-type demonitor_option() :: flush | {flush, boolean()} | info | {info, boolean()}.

-type atomvm_heap_growth_strategy() ::
    bounded_free
    | minimum
    | fibonacci.

-type spawn_option() ::
    {min_heap_size, pos_integer()}
    | {max_heap_size, pos_integer()}
    | {atomvm_heap_growth, atomvm_heap_growth_strategy()}
    | link
    | monitor.

-type send_destination() ::
    pid()
    | port()
    | atom().

% Current type until we make these references
-type resource() :: binary().

-type stacktrace_extrainfo() ::
    {line, pos_integer()}
    | {file, unicode:chardata()}
    | {error_info, #{module => module(), function => atom(), cause => term()}}
    | {atom(), term()}.

-type stacktrace() ::
    [
        {module(), atom(), arity() | [term()], [stacktrace_extrainfo()]}
        | {function(), arity() | [term()], [stacktrace_extrainfo()]}
    ].

%% Extended `stacktrace()' that can be passed to `raise/3'
-type raise_stacktrace() ::
    [{module(), atom(), arity() | [term()]} | {function(), arity() | [term()]}] | stacktrace().

%%-----------------------------------------------------------------------------
%% @param   Time time in milliseconds after which to send the timeout message.
%% @param   Dest Pid or server name to which to send the timeout message.
%% @param   Msg Message to send to Dest after Time ms.
%% @returns a reference that can be used to cancel the timer, if desired.
%% @doc     Start a timer, and send {timeout, TimerRef, Msg} to Dest after
%%          Time ms, where TimerRef is the reference returned from this function.
%% @end
%%-----------------------------------------------------------------------------
-spec start_timer(Time :: non_neg_integer(), Dest :: pid() | atom(), Msg :: term()) -> reference().
start_timer(Time, Dest, Msg) ->
    start_timer(Time, Dest, Msg, []).

%%-----------------------------------------------------------------------------
%% @hidden
%% @param   Time time in milliseconds after which to send the timeout message.
%% @param   Dest Pid or server name to which to send the timeout message.
%% @param   Msg Message to send to Dest after Time ms.
%% @param   Options options
%% @returns a reference that can be used to cancel the timer, if desired.
%% @doc     Start a timer, and send {timeout, TimerRef, Msg} to Dest after
%%          Time ms, where TimerRef is the reference returned from this function.
%%
%%          <em><b>Note.</b>  The Options argument is currently ignored.</em>
%% @end
%%-----------------------------------------------------------------------------
-spec start_timer(
    Time :: non_neg_integer(), Dest :: pid() | atom(), Msg :: term(), _Options :: list()
) -> reference().
start_timer(Time, Dest, Msg, _Options) ->
    timer_manager:start_timer(Time, Dest, Msg).

%%-----------------------------------------------------------------------------
%% @hidden
%% @param   Time time in milliseconds after which to send the timeout message.
%% @param   Dest Pid or server name to which to send the timeout message.
%% @param   Msg Message to send to Dest after Time ms.
%% @param   Options options
%% @returns a reference that can be used to cancel the timer, if desired.
%% @doc     Start a timer, and send {timeout, TimerRef, Msg} to Dest after
%%          Time ms, where TimerRef is the reference returned from this function.
%%
%%          <em><b>Note.</b>  The Options argument is currently ignored.</em>
%% @end
%%-----------------------------------------------------------------------------
-spec cancel_timer(TimerRef :: reference()) -> ok.
cancel_timer(TimerRef) ->
    timer_manager:cancel_timer(TimerRef).

%%-----------------------------------------------------------------------------
%% @param   Time time in milliseconds after which to send the message.
%% @param   Dest Pid or server name to which to send the message.
%% @param   Msg Message to send to Dest after Time ms.
%% @returns a reference that can be used to cancel the timer, if desired.
%% @doc     Send Msg to Dest after Time ms.
%% @end
%%-----------------------------------------------------------------------------
-spec send_after(Time :: non_neg_integer(), Dest :: pid() | atom(), Msg :: term()) -> reference().
send_after(Time, Dest, Msg) ->
    timer_manager:send_after(Time, Dest, Msg).

%%-----------------------------------------------------------------------------
%% @param   Pid the process pid.
%% @param   Key key used to find process information.
%% @returns process information for the specified pid defined by the specified key.
%% @doc     Return process information.
%%
%% This function returns information about the specified process.
%% The type of information returned is dependent on the specified key.
%%
%% The following keys are supported:
%% <ul>
%%      <li><b>heap_size</b> the number of words used in the heap (integer), including the stack but excluding fragments</li>
%%      <li><b>total_heap_size</b> the number of words used in the heap (integer) including fragments</li>
%%      <li><b>registered_name</b> - returns `{registered_name, RegisteredName}' where `RegisteredName' is the registered name of the port or process. If the port/process has no registered name, `[]' is returned</li>
%%      <li><b>stack_size</b> the number of words used in the stack (integer)</li>
%%      <li><b>message_queue_len</b> the number of messages enqueued for the process (integer)</li>
%%      <li><b>memory</b> the estimated total number of bytes in use by the process (integer)</li>
%%      <li><b>links</b> the list of linked processes</li>
%%      <li><b>monitored_by</b> the list of processes, NIF resources or ports that monitor the process</li>
%% </ul>
%% Specifying an unsupported term or atom raises a bad_arg error.
%%
%% @end
%%-----------------------------------------------------------------------------
-spec process_info
    (Pid :: pid(), heap_size) -> {heap_size, non_neg_integer()};
    (Pid :: pid(), total_heap_size) -> {total_heap_size, non_neg_integer()};
    (Pid :: pid(), registered_name) -> {registered_name, term()} | [];
    (Pid :: pid(), stack_size) -> {stack_size, non_neg_integer()};
    (Pid :: pid(), message_queue_len) -> {message_queue_len, non_neg_integer()};
    (Pid :: pid(), memory) -> {memory, non_neg_integer()};
    (Pid :: pid(), links) -> {links, [pid()]};
    (Pid :: pid(), monitored_by) -> {monitored_by, [pid() | resource() | port()]}.
process_info(_Pid, _Key) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Key key used to find system information.
%% @returns system information defined by the specified key.
%% @doc     Return system information.
%%
%% This function returns information about the system on which AtomVM is
%% running. The type of information returned is dependent on the specified key.
%%
%% The following keys are supported on all platforms:
%% <ul>
%%      <li><b>process_count</b> the number of processes running in the node (integer)</li>
%%      <li><b>port_count</b> the number of ports running in the node (integer)</li>
%%      <li><b>atom_count</b> the number of atoms currently allocated (integer)</li>
%%      <li><b>system_architecture</b> the processor and OS architecture (binary)</li>
%%      <li><b>version</b> the version of the AtomVM executable image (binary)</li>
%%      <li><b>wordsize</b> the number of bytes in a machine word on the current platform (integer)</li>
%%      <li><b>schedulers</b> the number of schedulers, equal to the number of online processors (integer)</li>
%%      <li><b>schedulers_online</b> the current number of schedulers (integer)</li>
%% </ul>
%% The following keys are supported on the ESP32 platform:
%% <ul>
%%      <li><b>esp32_free_heap_size</b> the number of (noncontiguous) free bytes in the ESP32 heap (integer)</li>
%%      <li><b>esp32_largest_free_block</b> the number of the largest contiguous free bytes in the ESP32 heap (integer)</li>
%%      <li><b>esp32_minimum_free_size</b> the smallest number of free bytes in the ESP32 heap since boot (integer)</li>
%%      <li><b>esp32_chip_info</b> Details about the model and capabilities of the ESP32 device (map)</li>
%% </ul>
%%
%% Additional keys may be supported on some platforms that are not documented here.
%%
%% Specifying an unsupported atom key will results in returning the atom 'undefined'.
%%
%% Specifying a term that is not an atom will result in a bad_arg error.
%%
%% @end
%%-----------------------------------------------------------------------------
-spec system_info(Key :: atom()) -> term().
system_info(_Key) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Key key used to change system flag.
%% @param   Value value to change
%% @returns previous value of the flag.
%% @doc     Update system flags.
%%
%% This function allows to modify system flags at runtime.
%%
%% The following key is supported on SMP builds:
%% <ul>
%%       <li><b>schedulers_online</b> the number of schedulers online</li>
%% </ul>
%%
%% Specifying an unsupported atom key will result in a bad_arg error.
%% Specifying a term that is not an atom will result in a bad_arg error.
%%
%% @end
%%-----------------------------------------------------------------------------
-spec system_flag(Key :: atom(), term()) -> term().
system_flag(_Key, _Value) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Data data to compute hash of, as a binary.
%% @returns the md5 hash of the input Data, as a 16-byte binary.
%% @doc     Computes the MD5 hash of an input binary, as defined by
%%          https://www.ietf.org/rfc/rfc1321.txt
%% @end
%%-----------------------------------------------------------------------------
-spec md5(Data :: binary()) -> binary().
md5(Data) when erlang:is_binary(Data) ->
    crypto:hash(md5, Data).

%%-----------------------------------------------------------------------------
%% @param   Module Name of module
%% @param   Function Exported function name
%% @param   Args Parameters to pass to function (max 6)
%% @returns Returns the result of Module:Function(Args).
%% @doc     Returns the result of applying Function in Module to Args. The applied
%%          function must be exported from Module. The arity of the function is the
%%          length of Args. Example:
%%          ```> apply(lists, reverse, [[a, b, c]]).
%%          [c,b,a]
%%          > apply(erlang, atom_to_list, ['AtomVM']).
%%          "AtomVM"'''
%%          If the number of arguments are known at compile time, the call is better
%%          written as Module:Function(Arg1, Arg2, ..., ArgN).
%% @end
%%-----------------------------------------------------------------------------
-spec apply(Module :: module(), Function :: function(), Args :: [term()]) -> term().
apply(Module, Function, Args) ->
    case Args of
        [] ->
            Module:Function();
        [Arg1] ->
            Module:Function(Arg1);
        [Arg1, Arg2] ->
            Module:Function(Arg1, Arg2);
        [Arg1, Arg2, Arg3] ->
            Module:Function(Arg1, Arg2, Arg3);
        [Arg1, Arg2, Arg3, Arg4] ->
            Module:Function(Arg1, Arg2, Arg3, Arg4);
        [Arg1, Arg2, Arg3, Arg4, Arg5] ->
            Module:Function(Arg1, Arg2, Arg3, Arg4, Arg5);
        [Arg1, Arg2, Arg3, Arg4, Arg5, Arg6] ->
            Module:Function(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6);
        _ ->
            erlang:error(badarg)
    end.

%%-----------------------------------------------------------------------------
%% @param   Function Function to call
%% @param   Args Parameters to pass to function (max 6)
%% @returns Returns the result of Function(Args).
%% @doc     Returns the result of applying Function to Args. The arity of the
%%          function is the length of Args. Example:
%%          ```> apply(fun(R) -> lists:reverse(R) end, [[a, b, c]]).
%%          [c,b,a]
%%          > apply(fun erlang:atom_to_list/1, ['AtomVM']).
%%          "AtomVM"'''
%%          If the number of arguments are known at compile time, the call is better
%%          written as Function(Arg1, Arg2, ..., ArgN).
%% @end
%%-----------------------------------------------------------------------------
-spec apply(Function :: fun(), Args :: [term()]) -> term().
apply(Function, Args) ->
    case Args of
        [] ->
            Function();
        [Arg1] ->
            Function(Arg1);
        [Arg1, Arg2] ->
            Function(Arg1, Arg2);
        [Arg1, Arg2, Arg3] ->
            Function(Arg1, Arg2, Arg3);
        [Arg1, Arg2, Arg3, Arg4] ->
            Function(Arg1, Arg2, Arg3, Arg4);
        [Arg1, Arg2, Arg3, Arg4, Arg5] ->
            Function(Arg1, Arg2, Arg3, Arg4, Arg5);
        [Arg1, Arg2, Arg3, Arg4, Arg5, Arg6] ->
            Function(Arg1, Arg2, Arg3, Arg4, Arg5, Arg6);
        _ ->
            erlang:error(badarg)
    end.

%%-----------------------------------------------------------------------------
%% @param   Map     the map to test
%% @returns `true' if `Map' is a map; `false', otherwise.
%% @doc     Return `true' if `Map' is a map; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_map(Map :: map()) -> boolean().
is_map(_Map) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term
%% @param   RecordTag    atom representing tuple tag
%%
%% @doc     Returns true if Term is a tuple and its first element is RecordTag, false otherwise.
%% @end
%%-----------------------------------------------------------------------------
-spec is_record(Term :: term(), RecordTag :: atom()) -> boolean().
is_record(_Term, _RecordTag) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Map the map
%% @returns the size of the map
%% @doc Returns the size of (i.e., the number of entries in) the map
%%
%% This function raises a `{badmap, Map}' error if `Map' is not a map.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec map_size(Map :: map()) -> non_neg_integer().
map_size(_Map) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Key     the key to get
%% @param   Map     the map from which to get the value
%% @returns the value in `Map' associated with `Key', if it exists.
%% @doc     Get the value in `Map' associated with `Key', if it exists.
%%
%% This function raises a `{badkey, Key}' error if 'Key' does not occur in
%% `Map' or a `{badmap, Map}' if `Map' is not a map.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec map_get(Key :: term(), Map :: map()) -> Value :: term().
map_get(_Key, _Map) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Key     the key
%% @param   Map     the map
%% @returns `true' if `Key' is associated with a value in `Map'; `false', otherwise.
%% @doc     Return `true' if `Key' is associated with a value in `Map'; `false', otherwise.
%%
%% This function raises a `{badmap, Map}' error if `Map' is not a map.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_map_key(Key :: term(), Map :: map()) -> boolean().
is_map_key(_Key, _Map) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   A   any term
%% @param   B   any term
%% @returns `A' if `A < B'; `B', otherwise.
%% @doc     Return the minimum value of two terms
%%
%% Terms are compared using `<' and follow the ordering principles defined in
%% https://www.erlang.org/doc/reference_manual/expressions.html#term-comparisons
%% @end
%%-----------------------------------------------------------------------------
-spec min(A :: any(), B :: any()) -> any().
min(_A, _B) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   A   any term
%% @param   B   any term
%% @returns `A' if `A > B'; `B', otherwise.
%% @doc     Return the maximum value of two terms
%%
%% Terms are compared using `>' and follow the ordering principles defined in
%% https://www.erlang.org/doc/reference_manual/expressions.html#term-comparisons
%% @end
%%-----------------------------------------------------------------------------
-spec max(A :: any(), B :: any()) -> any().
max(_A, _B) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Type the type of memory to request
%% @returns the amount of memory (in bytes) used of the specified type
%% @doc     Return the amount of memory (in bytes) used of the specified type
%%
%% @end
%%-----------------------------------------------------------------------------
-spec memory(Type :: mem_type()) -> non_neg_integer().
memory(_Type) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Unit    time unit
%% @returns monotonic time in the specified units
%% @doc     Return the monotonic time in the specified units.
%%
%% Monotonic time varies from system to system, and should not be used
%% to determine, for example the wall clock time.
%%
%% Instead, monotonic time should be used to compute time differences,
%% where the function is guaranteed to return a (not necessarily strictly)
%% monotonically increasing value.
%%
%% For example, on ESP32 system, monotonic time is reported as the difference from
%% the current time and the time the ESP32 device was started, whereas on UNIX
%% systems the value may vary among UNIX systems (e.g., Linux, macOS, FreeBSD).
%% @end
%%-----------------------------------------------------------------------------
-spec monotonic_time() -> integer().
monotonic_time() ->
    erlang:nif_error(undefined).

-spec monotonic_time(Unit :: time_unit()) -> integer().
monotonic_time(_Unit) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns the process directory
%% @doc     Return all values from the process dictionary
%% @end
%%-----------------------------------------------------------------------------
-spec get() -> [{any(), any()}].
get() ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Key     key in the process dictionary
%% @returns value associated with this key or undefined
%% @doc     Return a value associated with a given key in the process dictionary
%% @end
%%-----------------------------------------------------------------------------
-spec get(Key :: any()) -> any().
get(_Key) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Key     key to add to the process dictionary
%% @param   Value   value to store in the process dictionary
%% @returns the previous value associated with this key or undefined
%% @doc     Store a value with a given key in the process dictionary.
%% @end
%%-----------------------------------------------------------------------------
-spec put(Key :: any(), Value :: any()) -> any().
put(_Key, _Value) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns the previous process dictionary.
%% @doc     Erase all keys from the process dictionary.
%% @end
%%-----------------------------------------------------------------------------
-spec erase() -> [{any(), any()}].
erase() ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Key     key to erase from the process dictionary
%% @returns the previous value associated with this key or undefined
%% @doc     Erase a key from the process dictionary.
%% @end
%%-----------------------------------------------------------------------------
-spec erase(Key :: any()) -> any().
erase(_Key) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Module      module to test
%% @param   Function    function to test
%% @param   Arity       arity to test
%% @returns `true' if Module exports a Function with this Arity
%% @doc     Determine if a function is exported
%% @end
%%-----------------------------------------------------------------------------
-spec function_exported(Module :: module(), Function :: atom(), Arity :: arity()) -> boolean().
function_exported(_Module, _Function, _Arity) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns list of loaded modules
%% @doc     Returns all loaded modules.
%% @end
%%-----------------------------------------------------------------------------
-spec loaded() -> [module()].
loaded() ->
    erlang:nif_error(undefined).

%% @param   Module name of module
%% @returns boolean
%% @doc     Returns true if module is loaded without attempting to do it.
%% @end
-spec module_loaded(Module :: atom()) -> boolean().
module_loaded(_Module) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term    term to print
%% @returns `true'
%% @doc     Print a term to stdout.
%% @end
%%-----------------------------------------------------------------------------
-spec display(Term :: any()) -> true.
display(_Term) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   String  string to convert to an atom
%% @returns an atom from the string
%% @see     list_to_existing_atom/1
%% @doc     Convert a string into an atom.
%% Unlike Erlang/OTP 20+, atoms are limited to ISO-8859-1 characters. The VM
%% currently aborts if passed unicode characters.
%% Atoms are also limited to 255 characters. Errors with system_limit_atom if
%% the passed string is longer.
%% @end
%%-----------------------------------------------------------------------------
-spec list_to_atom(String :: string()) -> atom().
list_to_atom(_String) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   String  string to convert to an atom
%% @returns an atom from the string
%% @see     list_to_atom/1
%% @doc     Convert a string into an atom.
%% This function will error with badarg if the atom does not exist
%% @end
%%-----------------------------------------------------------------------------
-spec list_to_existing_atom(String :: string()) -> atom().
list_to_existing_atom(_String) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   IOList   iolist to convert to binary
%% @returns a binary composed of bytes and binaries from the list
%% @doc     Convert a list into a binary.
%% Errors with `badarg' if the list is not an iolist.
%% @end
%%-----------------------------------------------------------------------------
-spec list_to_binary(IOList :: iolist()) -> binary().
list_to_binary(_IOList) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   String  string to convert to integer
%% @returns an integer value from its string representation
%% @doc     Convert a string (list of characters) to integer.
%% Errors with `badarg' if the string is not a representation of an integer.
%% @end
%%-----------------------------------------------------------------------------
-spec list_to_integer(String :: string()) -> integer().
list_to_integer(_String) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   String  string to convert to integer
%% @param   Base  string to convert to integer
%% @returns an integer value from its string representation
%% @doc     Convert a string (list of characters) to integer in specified base.
%% Errors with `badarg' if the string is not a representation of an integer or
%% the base is out of bounds.
%% @end
%%-----------------------------------------------------------------------------
-spec list_to_integer(String :: string(), Base :: 2..36) -> integer().
list_to_integer(_String, _Base) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   List    list to convert to tuple
%% @returns a tuple with elements of the list
%% @doc     Convert a list to a tuple with the same size.
%% @end
%%-----------------------------------------------------------------------------
-spec list_to_tuple(List :: [any()]) -> tuple().
list_to_tuple(_List) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @equiv   byte_size(iolist_to_binary(IOList))
%% @param   IOList  IO list to compute the binary size of
%% @returns the number of bytes of IOList if it was convered to `binary()'
%% @doc     Compute the length in bytes of the IO list or `binary()'
%% @end
%%-----------------------------------------------------------------------------
-spec iolist_size(IOList :: iodata()) -> non_neg_integer().
iolist_size(_IOList) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   IOList  IO list to convert to binary
%% @returns a binary with the bytes of the IO list
%% @doc     Convert an IO list to binary.
%% @end
%%-----------------------------------------------------------------------------
-spec iolist_to_binary(IOList :: iodata()) -> binary().
iolist_to_binary(_IOList) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Binary  Binary to convert to atom
%% @returns an atom from passed binary
%% @doc     Convert a binary to atom, defaults to utf8.
%% @end
%%-----------------------------------------------------------------------------
-spec binary_to_atom(Binary :: binary()) -> atom().
binary_to_atom(_Binary) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Binary  Binary to convert to atom
%% @param   Encoding encoding for conversion (any of latin1, utf8 or unicode)
%% @returns an atom from passed binary
%% @doc     Convert a binary to atom.
%% @end
%%-----------------------------------------------------------------------------
-spec binary_to_atom(Binary :: binary(), Encoding :: atom_encoding()) -> atom().
binary_to_atom(_Binary, _Encoding) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Binary  Binary to parse for integer
%% @returns the integer represented by the binary
%% @doc     Parse the text in a given binary as an integer.
%% @end
%%-----------------------------------------------------------------------------
-spec binary_to_integer(Binary :: binary()) -> integer().
binary_to_integer(_Binary) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Binary  Binary to parse for integer
%% @returns the integer represented by the binary
%% @doc     Parse the text in a given binary as an integer.
%% @end
%%-----------------------------------------------------------------------------
-spec binary_to_integer(Binary :: binary(), Base :: 2..36) -> integer().
binary_to_integer(_Binary, _Base) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Binary  Binary to convert to list
%% @returns a list of bytes from the binary
%% @doc     Convert a binary to a list of bytes.
%% @end
%%-----------------------------------------------------------------------------
-spec binary_to_list(Binary :: binary()) -> [byte()].
binary_to_list(_Binary) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Atom        Atom to convert
%% @returns a binary with the atom's name
%% @doc     Convert an atom to a binary, defaults to utf8.
%% Only latin1 encoding is supported.
%% @end
%%-----------------------------------------------------------------------------
-spec atom_to_binary(Atom :: atom()) -> binary().
atom_to_binary(_Atom) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Atom        Atom to convert
%% @param   Encoding    Encoding for conversion (any of latin1, utf8 or unicode)
%% @returns a binary with the atom's name
%% @doc     Convert an atom to a binary.
%% Only latin1 encoding is supported.
%% @end
%%-----------------------------------------------------------------------------
-spec atom_to_binary(Atom :: atom(), Encoding :: atom_encoding()) -> binary().
atom_to_binary(_Atom, _Encoding) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Atom    Atom to convert
%% @returns a string with the atom's name
%% @doc     Convert an atom to a string.
%% @end
%%-----------------------------------------------------------------------------
-spec atom_to_list(Atom :: atom()) -> string().
atom_to_list(_Atom) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Float   Float to convert
%% @returns a binary with a text representation of the float
%% @doc     Convert a float to a binary.
%% @end
%%-----------------------------------------------------------------------------
-spec float_to_binary(Float :: float()) -> binary().
float_to_binary(_Float) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Float   Float to convert
%% @param   Options Options for conversion
%% @returns a binary with a text representation of the float
%% @doc     Convert a float to a binary.
%% @end
%%-----------------------------------------------------------------------------
-spec float_to_binary(Float :: float(), Options :: [float_format_option()]) -> binary().
float_to_binary(_Float, _Options) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Float   Float to convert
%% @returns a string with a text representation of the float
%% @doc     Convert a float to a string.
%% @end
%%-----------------------------------------------------------------------------
-spec float_to_list(Float :: float()) -> string().
float_to_list(_Float) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Float   Float to convert
%% @param   Options Options for conversion
%% @returns a string with a text representation of the float
%% @doc     Convert a float to a string.
%% @end
%%-----------------------------------------------------------------------------
-spec float_to_list(Float :: float(), Options :: [float_format_option()]) -> string().
float_to_list(_Float, _Options) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Fun     Function to get information about
%% @returns         Requested information about the function as a list of tuples.
%% @doc             Returns information about the function `Fun' in unspecified order.
%% @end
%%-----------------------------------------------------------------------------
fun_info(Fun) ->
    Items = [module, name, arity, type, env],
    lists:map(
        fun(Item) ->
            erlang:fun_info(Fun, Item)
        end,
        Items
    ).

%%-----------------------------------------------------------------------------
%% @param   Fun     Function to get information about
%% @param   Info    A list of atoms specifying the information to return.
%%                  Available atoms are: module, name, arity, type
%% @returns         Requested information about the function as a list of tuples.
%% @doc             Returns information about the function `Fun' in unspecified order.
%% @end
%%-----------------------------------------------------------------------------
fun_info(_Fun, _Info) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Integer integer to convert to a binary
%% @returns a binary with a text representation of the integer
%% @doc     Convert an integer to a binary.
%% @end
%%-----------------------------------------------------------------------------
-spec integer_to_binary(Integer :: integer()) -> binary().
integer_to_binary(_Integer) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Integer integer to convert to a binary
%% @param   Base    base for representation
%% @returns a binary with a text representation of the integer
%% @doc     Convert an integer to a binary.
%% @end
%%-----------------------------------------------------------------------------
-spec integer_to_binary(Integer :: integer(), Base :: 2..36) -> binary().
integer_to_binary(_Integer, _Base) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Integer integer to convert to a string
%% @returns a string representation of the integer
%% @doc     Convert an integer to a string.
%% @end
%%-----------------------------------------------------------------------------
-spec integer_to_list(Integer :: integer()) -> string().
integer_to_list(_Integer) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Integer integer to convert to a string
%% @param   Base    base for representation
%% @returns a string representation of the integer
%% @doc     Convert an integer to a string.
%% @end
%%-----------------------------------------------------------------------------
-spec integer_to_list(Integer :: integer(), Base :: 2..36) -> string().
integer_to_list(_Integer, _Base) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Fun     function to convert to a string
%% @returns a string representation of the function
%% @doc     Create a string representing a function.
%% @end
%%-----------------------------------------------------------------------------
-spec fun_to_list(Fun :: function()) -> string().
fun_to_list(_Fun) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Pid     pid to convert to a string
%% @returns a string representation of the pid
%% @doc     Create a string representing a pid.
%% @end
%%-----------------------------------------------------------------------------
-spec pid_to_list(Pid :: pid()) -> string().
pid_to_list(_Pid) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Port     port to convert to a string
%% @returns a string representation of the port
%% @doc     Create a string representing a port.
%% @end
%%-----------------------------------------------------------------------------
-spec port_to_list(Port :: port()) -> string().
port_to_list(_Port) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Ref     reference to convert to a string
%% @returns a string representation of the reference
%% @doc     Create a string representing a reference.
%% @end
%%-----------------------------------------------------------------------------
-spec ref_to_list(Ref :: reference()) -> string().
ref_to_list(_Ref) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Name        name of the process to register
%% @param   PidOrPort   pid or port of the process to register
%% @returns `true'
%% @doc     Register a name for a given process.
%% Errors with `badarg' if the name is already registered or if the process
%% is already registered.
%% @end
%%-----------------------------------------------------------------------------
-spec register(Name :: atom(), PidOrPort :: pid() | port()) -> true.
register(_Name, _PidOrPort) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Name    name to unregister
%% @returns `true'
%% @doc     Lookup a process by name.
%% Unlike Erlang/OTP, ports are not distinguished from processes.
%% Errors with `badarg' if the name is not registered.
%% @end
%%-----------------------------------------------------------------------------
-spec unregister(Name :: atom()) -> true.
unregister(_Name) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Name    name of the process to locate
%% @returns `undefined' or the pid of the registered process
%% @doc     Lookup a process by name.
%% @end
%%-----------------------------------------------------------------------------
-spec whereis(Name :: atom()) -> pid() | port() | undefined.
whereis(_Name) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Function    function to create a process from
%% @returns pid of the new process
%% @doc     Create a new process
%% @end
%%-----------------------------------------------------------------------------
-spec spawn(Function :: function()) -> pid().
spawn(Function) ->
    erlang:spawn_opt(Function, []).

%%-----------------------------------------------------------------------------
%% @param   Module      module of the function to create a process from
%% @param   Function    name of the function to create a process from
%% @param   Args        arguments to pass to the function to create a process from
%% @returns pid of the new process
%% @doc     Create a new process by calling exported Function from Module with Args.
%% @end
%%-----------------------------------------------------------------------------
-spec spawn(Module :: module(), Function :: atom(), Args :: [any()]) -> pid().
spawn(Module, Function, Args) ->
    erlang:spawn_opt(Module, Function, Args, []).

%%-----------------------------------------------------------------------------
%% @param   Function    function to create a process from
%% @returns pid of the new process
%% @doc     Create a new process and link it.
%% @end
%%-----------------------------------------------------------------------------
-spec spawn_link(Function :: function()) -> pid().
spawn_link(Function) ->
    erlang:spawn_opt(Function, [link]).

%%-----------------------------------------------------------------------------
%% @param   Module      module of the function to create a process from
%% @param   Function    name of the function to create a process from
%% @param   Args        arguments to pass to the function to create a process from
%% @returns pid of the new process
%% @doc     Create a new process by calling exported Function from Module with Args
%% and link it.
%% @end
%%-----------------------------------------------------------------------------
-spec spawn_link(Module :: module(), Function :: atom(), Args :: [any()]) -> pid().
spawn_link(Module, Function, Args) ->
    erlang:spawn_opt(Module, Function, Args, [link]).

%%-----------------------------------------------------------------------------
%% @param   Function    function to create a process from
%% @returns pid of the new process
%% @doc     Create a new process and monitor it.
%% @end
%%-----------------------------------------------------------------------------
-spec spawn_monitor(Function :: function()) -> pid().
spawn_monitor(Function) ->
    erlang:spawn_opt(Function, [monitor]).

%%-----------------------------------------------------------------------------
%% @param   Module      module of the function to create a process from
%% @param   Function    name of the function to create a process from
%% @param   Args        arguments to pass to the function to create a process from
%% @returns pid of the new process
%% @doc     Create a new process by calling exported Function from Module with Args
%%          and monitor it.
%% @end
%%-----------------------------------------------------------------------------
-spec spawn_monitor(Module :: module(), Function :: atom(), Args :: [any()]) -> pid().
spawn_monitor(Module, Function, Args) ->
    erlang:spawn_opt(Module, Function, Args, [monitor]).

%%-----------------------------------------------------------------------------
%% @param   Function    function to create a process from
%% @param   Options     additional options.
%% @returns pid of the new process
%% @doc     Create a new process.
%% @end
%%-----------------------------------------------------------------------------
-spec spawn_opt(Function :: function(), Options :: [spawn_option()]) ->
    pid() | {pid(), reference()}.
spawn_opt(_Name, _Options) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Module      module of the function to create a process from
%% @param   Function    name of the function to create a process from
%% @param   Args        arguments to pass to the function to create a process from
%% @param   Options     additional options.
%% @returns pid of the new process
%% @doc     Create a new process by calling exported Function from Module with Args.
%% @end
%%-----------------------------------------------------------------------------
-spec spawn_opt(
    Module :: module(), Function :: atom(), Args :: [any()], Options :: [spawn_option()]
) -> pid() | {pid(), reference()}.
spawn_opt(_Module, _Function, _Args, _Options) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Pid         process to link to
%% @returns `true'
%% @doc     Link current process with a given process.
%% @end
%%-----------------------------------------------------------------------------
-spec link(Pid :: pid()) -> true.
link(_Pid) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Pid         process to unlink from
%% @returns `true'
%% @doc     Unlink current process from a given process.
%% @end
%%-----------------------------------------------------------------------------
-spec unlink(Pid :: pid()) -> true.
unlink(_Pid) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns a new reference
%% @doc     Create a new reference
%% @end
%%-----------------------------------------------------------------------------
-spec make_ref() -> reference().
make_ref() ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Pid     process to send the message to
%% @param   Message message to send
%% @returns the sent message
%% @doc     Send a message to a given process
%% @end
%%-----------------------------------------------------------------------------
-spec send(Target :: send_destination(), Message :: Message) -> Message.
send(_Target, _Message) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Type        type of monitor to create
%% @param   PidOrPort   pid or port of the object to monitor
%% @returns a monitor reference
%% @doc     Create a monitor on a process or on a port.
%% When the process or the port terminates, the following message is sent to
%% the caller of this function:
%% ```
%% {'DOWN', MonitorRef, Type, Pid, Reason}
%% '''
%% Unlike Erlang/OTP, monitors are only supported for processes and ports.
%% @end
%%-----------------------------------------------------------------------------
-spec monitor
    (Type :: process, Pid :: pid() | atom()) -> reference();
    (Type :: port, Port :: port() | atom()) -> reference().
monitor(_Type, _PidOrPort) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Monitor reference of monitor to remove
%% @returns `true'
%% @doc     Remove a monitor
%% @end
%%-----------------------------------------------------------------------------
-spec demonitor(Monitor :: reference()) -> true.
demonitor(_Monitor) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Monitor reference of monitor to remove
%% @param   Options options list
%% @returns `true'
%% @doc     Remove a monitor, with options.
%% If `flush', monitor messages are flushed and guaranteed to not be received.
%% If `info', return `true' if monitor was removed, `false' if it was not found.
%% If both options are provivded, return `false' if flush was needed.
%% @end
%%-----------------------------------------------------------------------------
-spec demonitor(Monitor :: reference(), Options :: [demonitor_option()]) -> boolean().
demonitor(_Monitor, _Options) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Reason  reason for exit
%% @doc     Raises an exception of class `exit' with reason `Reason'.
%% The exception can be caught. If it is not, the process exits.
%% If the exception is not caught the signal is sent to linked processes.
%% In this case, if `Reason' is `kill', it is not transformed into `killed' and
%% linked processes can trap it (unlike `exit/2').
%% @end
%%-----------------------------------------------------------------------------
-spec exit(Reason :: any()) -> no_return().
exit(_Reason) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Process target process
%% @param   Reason  reason for exit
%% @returns `true'
%% @doc     Send an exit signal to target process.
%% The consequences of the exit signal depends on `Reason', on whether
%% `Process' is self() or another process and whether target process is
%% trapping exit.
%% If `Reason' is not `kill' nor `normal':
%% <ul>
%%     <li>If target process is not trapping exits, it exits with `Reason'</li>
%%     <li>If traget process is trapping exits, it receives a message
%%         ``{'EXIT', From, Reason}'' where `From' is the caller of `exit/2'.</li>
%% </ul>
%% If `Reason' is `kill', the target process exits with `Reason' changed to
%% `killed'.
%% If `Reason' is `normal' and `Process' is not `self()':
%% <ul>
%%     <li>If target process is not trapping exits, nothing happens.</li>
%%     <li>If traget process is trapping exits, it receives a message
%%         ``{'EXIT', From, normal}'' where `From' is the caller of `exit/2'.</li>
%% </ul>
%% If `Reason' is `normal' and `Process' is `self()':
%% <ul>
%%     <li>If target process is not trapping exits, it exits with `normal'.</li>
%%     <li>If traget process is trapping exits, it receives a message
%%         ``{'EXIT', From, normal}'' where `From' is the caller of `exit/2'.</li>
%% </ul>
%% @end
%%-----------------------------------------------------------------------------
-spec exit(Process :: pid(), Reason :: any()) -> true.
exit(_Process, _Reason) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   PortName    Tuple {spawn, Name} identifying the port
%% @param   Options     Options, meaningful for the port
%% @returns A pid identifying the open port
%% @doc     Open a port.
%% Unlike Erlang/OTP, ports are identified by pids.
%% @end
%%-----------------------------------------------------------------------------
-spec open_port(PortName :: {spawn, iodata()}, Options :: [any()] | map()) -> port().
open_port(_PortName, _Options) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns An integer representing system time.
%% @doc     Returns the current Erlang system time in native time unit.
%% @end
%%-----------------------------------------------------------------------------
-spec system_time() -> integer().
system_time() ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Unit    Unit to return system time in
%% @returns An integer representing system time
%% @doc     Get the current system time in provided unit.
%% @end
%%-----------------------------------------------------------------------------
-spec system_time(Unit :: time_unit()) -> non_neg_integer().
system_time(_Unit) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns Pid of group leader or self() if no group leader is set.
%% @doc     Return the pid of the group leader of caller.
%% @end
%%-----------------------------------------------------------------------------
-spec group_leader() -> pid().
group_leader() ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Leader  pid of process to set as leader
%% @param   Pid     pid of process to set a Leader
%% @returns `true'
%% @doc Set the group leader for a given process.
%% @end
%%-----------------------------------------------------------------------------
-spec group_leader(Leader :: pid(), Pid :: pid()) -> true.
group_leader(_Leader, _Pid) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Flag    flag to change
%% @param   Value   new value of the flag
%% @returns Previous value of the flag
%% @doc     Set a flag for the current process.
%% When `trap_exit' is true, exit signals are converted to messages
%% ```
%% {'EXIT', From, Reason}
%% '''
%% and the process does not exit if `Reason' is not `normal'.
%%
%% @end
%%-----------------------------------------------------------------------------
-spec process_flag(Flag :: trap_exit, Value :: boolean()) -> pid().
process_flag(_Flag, _Value) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Module  module to get info for
%% @returns A list of module info tuples
%% @doc     Get info for a given module.
%% This function is not meant to be called directly but through
%% `Module:module_info/0' exported function.
%%
%% @end
%%-----------------------------------------------------------------------------
-spec get_module_info(Module :: atom()) -> [{atom(), any()}].
get_module_info(_Module) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Module  module to get info for
%% @param   InfoKey info to get
%% @returns A term representing info for given module
%% @doc     Get specific info for a given module.
%% This function is not meant to be called directly but through
%% `Module:module_info/1' exported function.
%% Supported info keys are `module', `exports', `compile' and `attributes'.
%%
%% @end
%%-----------------------------------------------------------------------------
-spec get_module_info(Module :: atom(), InfoKey :: atom()) -> any().
get_module_info(_Module, _InfoKey) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns A list of pids of all processes
%% @doc     Return a list of all current processes.
%% Compared to Erlang/OTP, this function also returns native processes (ports).
%% @end
%%-----------------------------------------------------------------------------
-spec processes() -> [pid()].
processes() ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns `true' if the process is alive, `false' otherwise
%% @param   Pid     pid of the process to test
%% @doc     Determine if a process is alive
%% @end
%%-----------------------------------------------------------------------------
-spec is_process_alive(Pid :: pid()) -> boolean().
is_process_alive(_Pid) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns `true'
%% @doc     Run a garbage collect in current process
%% @end
%%-----------------------------------------------------------------------------
-spec garbage_collect() -> true.
garbage_collect() ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns `true' or `false' if the process no longer exists
%% @param   Pid     pid of the process to garbage collect
%% @doc     Run a garbage collect in a given process.
%% The function returns before the garbage collect actually happens.
%% @end
%%-----------------------------------------------------------------------------
-spec garbage_collect(Pid :: pid()) -> boolean().
garbage_collect(_Pid) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns A term decoded from passed binary
%% @param   Binary  binary to decode
%% @doc Decode a term that was previously encodes with `term_to_binary/1'
%% This function should be mostly compatible with its Erlang/OTP counterpart.
%% Unlike modern Erlang/OTP, resources are currently serialized as empty
%% binaries and cannot be unserialized.
%% @end
%%-----------------------------------------------------------------------------
-spec binary_to_term(Binary :: binary()) -> any().
binary_to_term(_Binary) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns A binary encoding passed term.
%% @param   Term    term to encode
%% @doc Encode a term to a binary that can later be decoded with `binary_to_term/1'.
%% This function should be mostly compatible with its Erlang/OTP counterpart.
%% Unlike modern Erlang/OTP, resources are currently serialized as empty
%% binaries.
%% @end
%%-----------------------------------------------------------------------------
-spec term_to_binary(Term :: any()) -> binary().
term_to_binary(_Term) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns A tuple with two subbinaries
%% @param   Bin    binary to split
%% @param   Pos    position to split the binary, from 0 to `byte_size(Bin)'
%% @doc Split a binary into two sub-binaries. This operation is not destructive
%% and will create two new binaries.
%% @end
%%-----------------------------------------------------------------------------
-spec split_binary(Bin :: binary(), Pos :: non_neg_integer()) -> {binary(), binary()}.
split_binary(_Bin, _Pos) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Data iodata to compute the CRC32 checksum of
%% @returns the CRC32 checksum as a non-negative integer
%% @doc Computes the CRC32 (IEEE 802.3) checksum for `Data'.
%% @end
%%-----------------------------------------------------------------------------
-spec crc32(Data :: iodata()) -> non_neg_integer().
crc32(_Data) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   OldCrc a previously computed CRC32 checksum
%% @param   Data iodata to update the checksum with
%% @returns the updated CRC32 checksum as a non-negative integer
%% @doc Continues computing a CRC32 checksum by combining a previous checksum
%% with the checksum of new data.
%% @end
%%-----------------------------------------------------------------------------
-spec crc32(OldCrc :: non_neg_integer(), Data :: iodata()) -> non_neg_integer().
crc32(_OldCrc, _Data) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   FirstCrc CRC32 checksum of the first data block
%% @param   SecondCrc CRC32 checksum of the second data block
%% @param   SecondSize byte size of the second data block
%% @returns the combined CRC32 checksum as a non-negative integer
%% @doc Combines two previously computed CRC32 checksums. This is equivalent
%% to computing the CRC32 of the concatenation of the two data blocks.
%% @end
%%-----------------------------------------------------------------------------
-spec crc32_combine(
    FirstCrc :: non_neg_integer(), SecondCrc :: non_neg_integer(), SecondSize :: non_neg_integer()
) -> non_neg_integer().
crc32_combine(_FirstCrc, _SecondCrc, _SecondSize) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns A tuple representing the current timestamp.
%% @see monotonic_time/1
%% @see system_time/1
%% @doc Return the timestamp in `{MegaSec, Sec, MicroSec}' format.
%% This the old format returned by `erlang:now/0'. Please note that the latter
%% which is deprecated in Erlang/OTP is not implemented by AtomVM.
%% @end
%%-----------------------------------------------------------------------------
-spec timestamp() -> erlang:timestamp().
timestamp() ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns A tuple representing the current universal time.
%% @see localtime/0
%% @doc Return the current time and day for UTC.
%% @end
%%-----------------------------------------------------------------------------
-spec universaltime() -> calendar:datetime().
universaltime() ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns A tuple representing the current local time.
%% @see universaltime/0
%% @doc Return the current time and day for system local timezone.
%% @end
%%-----------------------------------------------------------------------------
-spec localtime() -> calendar:datetime().
localtime() ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns A tuple representing the current local time.
%% @see universaltime/0
%% @param TZ a timezone, as understood by C library, for example
%% `"CET-1CEST,M3.5.0,M10.5.0/3"' for esp-idf for Paris
%% @doc Return the current time and day for a given timezone.
%% @end
%%-----------------------------------------------------------------------------
-spec localtime(TZ :: string()) -> calendar:datetime().
localtime(_TZ) ->
    erlang:nif_error(undefined).

%% @hidden
-spec setnode(atom(), non_neg_integer()) -> true.
setnode(_NodeName, _Creation) ->
    erlang:nif_error(undefined).

%% @hidden
-spec setnode(node(), pid(), {non_neg_integer(), non_neg_integer()}) -> reference().
setnode(_TargetNode, _ConnPid, _TargetFlagsCreation) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns true if current node is part of distribution
%% @doc     Determine if current node is part of distribution
%% @end
-spec is_alive() -> boolean().
is_alive() ->
    erlang:node() =/= nonode@nohost.

%% @hidden
-spec dist_ctrl_get_data_notification(binary()) -> ok.
dist_ctrl_get_data_notification(_DHandle) ->
    erlang:nif_error(undefined).

%% @hidden
-spec dist_ctrl_get_data(binary()) -> none | binary().
dist_ctrl_get_data(_DHandle) ->
    erlang:nif_error(undefined).

%% @hidden
-spec dist_ctrl_put_data(binary(), binary()) -> ok.
dist_ctrl_put_data(_DHandle, _Packet) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns The cookie used by this node
%% @doc Return the cookie used by this node or `nocookie' if
%% distribution was not started
%% @end
%%-----------------------------------------------------------------------------
-spec get_cookie() -> nocookie | atom().
get_cookie() ->
    try
        CookieBin = net_kernel:get_cookie(),
        ?MODULE:binary_to_atom(CookieBin, latin1)
    catch
        exit:{Reason, _} when
            Reason =:= noproc;
            Reason =:= shutdown;
            Reason =:= killed
        ->
            nocookie
    end.

%%-----------------------------------------------------------------------------
%% @returns The cookie used to connect to another node
%% @doc Return the cookie used to connect to another node or `nocookie' if
%% distribution was not started
%% @end
%%-----------------------------------------------------------------------------
-spec get_cookie(node()) -> nocookie | atom().
get_cookie(Node) ->
    try
        CookieBin = net_kernel:get_cookie(Node),
        ?MODULE:binary_to_atom(CookieBin, latin1)
    catch
        exit:{Reason, _} when
            Reason =:= noproc;
            Reason =:= shutdown;
            Reason =:= killed
        ->
            nocookie
    end.

%%-----------------------------------------------------------------------------
%% @param Cookie the cookie to use to connect to this node
%% @doc Set the cookie of this node. Fails if distribution was not started.
%% @end
%%-----------------------------------------------------------------------------
-spec set_cookie(Cookie :: atom()) -> ok.
set_cookie(Cookie) ->
    net_kernel:set_cookie(?MODULE:atom_to_binary(Cookie, latin1)).

%%-----------------------------------------------------------------------------
%% @param Node the node to set the cookie for
%% @param Cookie the cookie to use to connect to Node
%% @doc Set the cookie of a given node. Fails if distribution was not started.
%% @end
%%-----------------------------------------------------------------------------
-spec set_cookie(Node :: node(), Cookie :: atom()) -> ok.
set_cookie(Node, Cookie) ->
    net_kernel:set_cookie(Node, ?MODULE:atom_to_binary(Cookie, latin1)).

%%-----------------------------------------------------------------------------
%% @returns A unique integer
%% @doc     Same as erlang:unique_integer([]).
%% @end
%%-----------------------------------------------------------------------------
-spec unique_integer() -> integer().
unique_integer() ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Options list of options.
%% @returns a unique integer
%% @doc     Return a unique integer. If positive is passed, returned integer is
%%          positive. If monotonic is passed, returned integer is monotonically increasing
%%          across all processes.
%% @end
%%-----------------------------------------------------------------------------
-spec unique_integer([monotonic | positive]) -> integer().
unique_integer(_Options) ->
    erlang:nif_error(undefined).

%% @private
-spec nif_error(Reason :: any()) -> no_return().
nif_error(_Reason) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Class exception class to raise
%% @param   Reason reason of the exception
%% @param   Stacktrace list as provided by a try/catch clause
%% @returns badarg if arguments are invalid
%% @doc     Raise an exception of the specified class or return badarg if
%%          arguments are invalid. To be sure to not return, the pattern
%%          `error(erlang:raise(Class, Reason, Stacktrace))' is common.
%% @end
%%-----------------------------------------------------------------------------
-spec raise(error | exit | throw, Reason :: term(), Stacktrace :: raise_stacktrace()) ->
    badarg.
raise(_Class, _Reason, _Stacktrace) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Number  the number to get the absolute value of
%% @returns the absolute value of `Number'
%% @doc     Return the absolute value of `Number'.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec abs(Number) -> Number when Number :: number().
abs(_Number) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Bitstring  the bitstring to get the byte size of
%% @returns the number of bytes needed to contain `Bitstring'
%% @doc     Return the number of bytes needed to contain `Bitstring'.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec byte_size(Bitstring :: bitstring()) -> non_neg_integer().
byte_size(_Bitstring) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   N       the one-based index of the element to return
%% @param   Tuple   the tuple from which to get the element
%% @returns the element at the given one-based index in the tuple
%% @doc     Return the element at the given one-based index in a tuple.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec element(N :: pos_integer(), Tuple :: tuple()) -> term().
element(_N, _Tuple) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Reason  the reason for the error
%% @doc     Raises an exception of class `error' with reason `Reason'.
%%
%% The stacktrace is automatically added.
%% @end
%%-----------------------------------------------------------------------------
-spec error(Reason :: term()) -> no_return().
error(_Reason) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Reason  the reason for the error
%% @param   Args    the argument list, or a list of extra error information
%% @doc     Raises an exception of class `error' with reason `Reason' and
%%          arguments or extra error info `Args'.
%%
%% The stacktrace is automatically added.
%% @end
%%-----------------------------------------------------------------------------
-spec error(Reason :: term(), Args :: [term()] | none) -> no_return().
error(_Reason, _Args) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   List    a nonempty list
%% @returns the first element (head) of the list
%% @doc     Return the first element of a list.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec hd(List :: nonempty_list()) -> term().
hd(_List) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term  the term to test
%% @returns `true' if `Term' is an atom; `false', otherwise.
%% @doc     Return `true' if `Term' is an atom; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_atom(Term :: term()) -> boolean().
is_atom(_Term) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term  the term to test
%% @returns `true' if `Term' is a binary; `false', otherwise.
%% @doc     Return `true' if `Term' is a binary; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_binary(Term :: term()) -> boolean().
is_binary(_Term) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term  the term to test
%% @returns `true' if `Term' is a boolean (`true' or `false'); `false', otherwise.
%% @doc     Return `true' if `Term' is a boolean; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_boolean(Term :: term()) -> boolean().
is_boolean(_Term) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term  the term to test
%% @returns `true' if `Term' is a float; `false', otherwise.
%% @doc     Return `true' if `Term' is a floating point number; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_float(Term :: term()) -> boolean().
is_float(_Term) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term  the term to test
%% @returns `true' if `Term' is a function; `false', otherwise.
%% @doc     Return `true' if `Term' is a function; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_function(Term :: term()) -> boolean().
is_function(_Term) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term    the term to test
%% @param   Arity   the expected arity
%% @returns `true' if `Term' is a function with arity `Arity'; `false', otherwise.
%% @doc     Return `true' if `Term' is a function with the given arity; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_function(Term :: term(), Arity :: non_neg_integer()) -> boolean().
is_function(_Term, _Arity) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term  the term to test
%% @returns `true' if `Term' is an integer; `false', otherwise.
%% @doc     Return `true' if `Term' is an integer; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_integer(Term :: term()) -> boolean().
is_integer(_Term) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term  the term to test
%% @param   LB    lower bound
%% @param   LB    upper bound
%% @returns `true' if `Term' is an integer between `LB` and `UB` inclusive,
%%          `false', otherwise.
%% @doc     Return `true' if `Term' is an integer between `LB` and `UB`
%%          inclusive; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_integer(Term :: term(), LB :: integer(), UB :: integer()) -> boolean().
is_integer(_Term, _LB, _UB) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term  the term to test
%% @returns `true' if `Term' is a list (including the empty list); `false', otherwise.
%% @doc     Return `true' if `Term' is a list; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_list(Term :: term()) -> boolean().
is_list(_Term) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term  the term to test
%% @returns `true' if `Term' is a number (integer or float); `false', otherwise.
%% @doc     Return `true' if `Term' is a number; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_number(Term :: term()) -> boolean().
is_number(_Term) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term  the term to test
%% @returns `true' if `Term' is a pid; `false', otherwise.
%% @doc     Return `true' if `Term' is a process identifier; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_pid(Term :: term()) -> boolean().
is_pid(_Term) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term  the term to test
%% @returns `true' if `Term' is a reference; `false', otherwise.
%% @doc     Return `true' if `Term' is a reference; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_reference(Term :: term()) -> boolean().
is_reference(_Term) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Term  the term to test
%% @returns `true' if `Term' is a tuple; `false', otherwise.
%% @doc     Return `true' if `Term' is a tuple; `false', otherwise.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec is_tuple(Term :: term()) -> boolean().
is_tuple(_Term) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   List  the list to get the length of
%% @returns the length of the list
%% @doc     Return the length of a list.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec length(List :: list()) -> non_neg_integer().
length(_List) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   String  a string representation of a float
%% @returns the float represented by the string
%% @doc     Parse a string to a floating point number.
%% Errors with `badarg' if the string is not a valid float representation.
%% @end
%%-----------------------------------------------------------------------------
-spec list_to_float(String :: string()) -> float().
list_to_float(_String) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns the node name of the local node
%% @doc     Return the name of the local node.
%% If the node is not alive, `nonode@nohost' is returned.
%% @end
%%-----------------------------------------------------------------------------
-spec node() -> node().
node() ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Number  the number to round
%% @returns `Number' rounded to the nearest integer
%% @doc     Return the integer closest to `Number'.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec round(Number :: number()) -> integer().
round(_Number) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @returns the pid of the calling process
%% @doc     Return the pid of the calling process.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec self() -> pid().
self() ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Index   the one-based index of the element to replace
%% @param   Tuple   the original tuple
%% @param   Value   the new value to set
%% @returns a new tuple with the element at position `Index' replaced by `Value'
%% @doc     Return a new tuple with element at `Index' replaced by `Value'.
%% @end
%%-----------------------------------------------------------------------------
-spec setelement(Index :: pos_integer(), Tuple :: tuple(), Value :: term()) -> tuple().
setelement(_Index, _Tuple, _Value) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   TupleOrBinary  a tuple or a binary
%% @returns the size of the tuple or binary
%% @doc     Return the size of a tuple or binary.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec size(TupleOrBinary :: tuple() | binary()) -> non_neg_integer().
size(_TupleOrBinary) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Reason  the reason for the exception
%% @doc     Raises an exception of class `throw' with reason `Reason'.
%% @end
%%-----------------------------------------------------------------------------
-spec throw(Reason :: term()) -> no_return().
throw(_Reason) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   List    a nonempty list
%% @returns the tail of the list (all elements except the first)
%% @doc     Return the tail of a list (the list without its first element).
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec tl(List :: nonempty_list()) -> term().
tl(_List) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Number  the number to truncate
%% @returns the integer part of `Number'
%% @doc     Return the integer part of `Number' by truncating towards zero.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec trunc(Number :: number()) -> integer().
trunc(_Number) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Tuple  the tuple to get the size of
%% @returns the number of elements in the tuple
%% @doc     Return the number of elements in a tuple.
%%
%% This function may be used in a guard expression.
%% @end
%%-----------------------------------------------------------------------------
-spec tuple_size(Tuple :: tuple()) -> non_neg_integer().
tuple_size(_Tuple) ->
    erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param   Tuple  the tuple to convert
%% @returns a list containing the elements of the tuple
%% @doc     Convert a tuple to a list of its elements.
%% @end
%%-----------------------------------------------------------------------------
-spec tuple_to_list(Tuple :: tuple()) -> [term()].
tuple_to_list(_Tuple) ->
    erlang:nif_error(undefined).
