%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1998-2007, 2010 The University of Melbourne.
% Copyright (C) 2014-2022 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%---------------------------------------------------------------------------%
%
% File: table_builtin.m.
% Main authors: zs, fjh, ohutch.
% Stability: low.
%
% This file is automatically imported, as if via ":- use_module:, into every
% module that contains a tabling pragma (`pragma loopcheck', `pragma memo',
% or `pragma minimal_model'). It is intended for the builtin procedures
% that the compiler generates implicit calls to when implementing tabling.
% This is separated from private_builtin.m, partly for modularity, but
% mostly to improve compilation speed for programs that don't use tabling.
%
% The *_shortcut predicates are dummies. They do not ever get called directly;
% their purpose is to serve as hooks on which to hang foreign_procs generated
% directly by the compiler.
%
% This module is a private part of the Mercury implementation; user modules
% should never explicitly import this module. The interface for this module
% does not get included in the Mercury library reference manual.
%
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%

:- module table_builtin.
:- interface.

% This section of the module contains the predicates that are
% automatically inserted by the table_gen pass of the compiler
% into predicates that use tabling, and the types they use.
%
% The predicates fall into the following categories:
%
% - Predicates that manage loop checking.
%
% - Predicates that manage memoization in general.
%
% - Predicates that manage memoization of I/O procedures.
%
% - Predicates that manage minimal model tabling using stack copying.
%
% - Predicates that manage minimal model tabling using own stacks.
%
% - Utility predicates that are needed in the tabling of all predicates.
%
% The utility predicates that handle tries are combined lookup/insert
% operations; if the item being searched for is not already in the trie,
% they insert it. These predicates are used to implement both call tables,
% in which case the items inserted are input arguments of a tabled predicate,
% and answer tables, in which case the items inserted are output arguments
% of a tabled predicate.
%
% The call table trie is used for detecting duplicate calls,
% while the answer table trie is used for detecting duplicate answers.
% However, storing answers only in the answer table trie is not sufficient,
% for two reasons. First, while the trie encodes the values of the output
% arguments, this encoding is not in the form of the native Mercury
% representations of those arguments. Second, for model_non subgoals we
% want a chronological list of answers, to allow us to separate out
% answers we have returned already from answers we have not yet returned.
% To handle the first problem, we save each answer not only in the
% answer table trie but also in an answer block, which is a vector of N
% elements, where N is the number of output arguments of the procedure
% concerned. To handle the second problem, for model_non procedures
% we chain these answer blocks together in a chronological list.
%
% For loopcheck goals, the word at the end of the call table trie
% is used as a status indication (of type loop_status).
%
% For memo goals, the word at the end of the call table trie is used
% both as a status indication (of type memo_status) and as a pointer to
% an answer block (if the goal succeeded). This is OK, because we can
% distinguish the two, and because an answer block pointer can be
% associated with only one status value (memo_succeeded).
%
% For minimal model goals with stack copying, the word at the end of the call
% table trie always points to a subgoal structure, with several fields. The
% status of the subgoal and the list of answers are two of these fields. Other
% fields, described in runtime/mercury_minimal_model.h, are used in the
% implementation of the minimal model.
%
% For minimal model goals with own stacks, the word at the end of the call
% table trie always points to a consumer structure, with several fields. The
% status of the consumer and the list of answers are two of these fields. Other
% fields, described in runtime/mercury_mm_own_stack.h, are used in the
% implementation of the minimal model.
%
% All of the predicates here with the impure declaration modify the tabling
% structures. Because the structures are persistent through backtracking,
% this causes the predicates to become impure. The predicates with the semipure
% directive only examine the tabling structures, but do not modify them.
%
% At the moment, tabling is supported only by the LLDS and MLDS C backends, so
% in the next three type definitions, only the C definition is useful.  The
% Mercury definitions are placeholders only, required to make this module
% compile cleanly on the non-C backends respectively.

    % This type represents the interior pointers of both call
    % tables and answer tables.
    %
:- type ml_trie_node.

    % This type represents the blocks we use to store sets of output arguments.
    %
:- type ml_answer_block.

    % This type represents the data structure the implementation uses to store
    % information related to a given procedure.
    %
:- type ml_proc_table_info.

    % N.B. interface continued below

%---------------------------------------------------------------------------%

:- implementation.

    % This type represents the interior pointers of both call tables
    % and answer tables.
    %
:- type ml_trie_node
    --->    ml_trie_node(c_pointer).
:- pragma foreign_type("C", ml_trie_node, "MR_TrieNode",
    [can_pass_as_mercury_type]).

    % This type represents a block of memory that contains one word
    % for each output argument of a procedure.
    %
:- type ml_answer_block
    --->    ml_answer_block(c_pointer).
:- pragma foreign_type("C", ml_answer_block, "MR_AnswerBlock",
    [can_pass_as_mercury_type]).

:- type ml_proc_table_info
    --->    ml_proc_table_info(c_pointer).
:- pragma foreign_type("C", ml_proc_table_info, "MR_ProcTableInfoPtr",
    [can_pass_as_mercury_type]).

%---------------------------------------------------------------------------%

:- interface.

%
% Predicates that manage loop checking.
%

    % This type should correspond exactly to the MR_LOOPCHECK_* #defines
    % in runtime/mercury_tabling.h.
    %
:- type loop_status
    --->    loop_inactive
    ;       loop_active.

:- impure pred table_loop_setup(ml_trie_node::in, loop_status::out) is det.

:- impure pred table_loop_setup_shortcut(ml_trie_node::in, ml_trie_node::out,
    loop_status::out) is det.

    % Mark the call represented by the given table as currently
    % not being evaluated.
    %
:- impure pred table_loop_mark_as_inactive(ml_trie_node::in) is det.

    % Mark the call represented by the given table as currently
    % not being evaluated, and fail.
    %
:- impure pred table_loop_mark_as_inactive_and_fail(ml_trie_node::in)
    is failure.

    % Mark the call represented by the given table as currently
    % being evaluated, and fail.
    %
:- impure pred table_loop_mark_as_active_and_fail(ml_trie_node::in) is failure.

    % N.B. interface continued below

%---------------------------------------------------------------------------%

:- implementation.

:- pragma foreign_proc("C",
    table_loop_setup(T::in, Status::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_loop_setup(MR_TABLE_DEBUG_BOOL, MR_FALSE, T, Status);
").

table_loop_setup(_, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_loop_setup").

:- pragma foreign_proc("C",
    table_loop_setup_shortcut(T0::in, T::out, Status::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_loop_setup_shortcut(T0, T, Status);
").

table_loop_setup_shortcut(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_loop_setup_shortcut").

:- pragma foreign_proc("C",
    table_loop_mark_as_inactive(T::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_loop_mark_as_inactive(MR_TABLE_DEBUG_BOOL, T);
").

table_loop_mark_as_inactive(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_loop_mark_as_inactive").

:- pragma foreign_proc("C",
    table_loop_mark_as_inactive_and_fail(T::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_loop_mark_as_inactive_and_fail(MR_TABLE_DEBUG_BOOL, T);
").

table_loop_mark_as_inactive_and_fail(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_loop_mark_as_inactive"),
    fail.

:- pragma foreign_proc("C",
    table_loop_mark_as_active_and_fail(T::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_loop_mark_as_active_and_fail(MR_TABLE_DEBUG_BOOL, T);
").

table_loop_mark_as_active_and_fail(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_loop_mark_as_active"),
    fail.

%---------------------------------------------------------------------------%

:- interface.

%
% Predicates that manage memoization.
%

    % This type should correspond exactly to the MR_MEMO_DET_* #defines
    % in runtime/mercury_tabling.h.
    %
:- type memo_det_status
    --->    memo_det_inactive
    ;       memo_det_active
    ;       memo_det_succeeded.

    % This type should correspond exactly to the MR_MEMO_SEMI_* #defines
    % in runtime/mercury_tabling.h.
    %
:- type memo_semi_status
    --->    memo_semi_inactive
    ;       memo_semi_active
    ;       memo_semi_succeeded
    ;       memo_semi_failed.

    % This type should correspond exactly to the MR_MemoNonStatus type
    % in runtime/mercury_tabling.h.
    %
:- type memo_non_status
    --->    memo_non_inactive
    ;       memo_non_active
    ;       memo_non_incomplete
    ;       memo_non_complete.

:- type memo_non_record.

:- impure pred table_memo_det_setup(ml_trie_node::in, memo_det_status::out)
    is det.

:- impure pred table_memo_det_setup_shortcut(ml_trie_node::in,
    ml_trie_node::out, memo_det_status::out) is det.

:- impure pred table_memo_semi_setup(ml_trie_node::in, memo_semi_status::out)
    is det.

:- impure pred table_memo_semi_setup_shortcut(ml_trie_node::in,
    ml_trie_node::out, memo_semi_status::out) is det.

:- impure pred table_memo_non_setup(ml_trie_node::in,
    memo_non_record::out, memo_non_status::out) is det.

    % Save the fact that the call has failed in the given table.
    %
:- impure pred table_memo_mark_as_failed(ml_trie_node::in) is failure.

    % Save the fact that the call has succeeded in the given table.
    %
:- impure pred table_memo_mark_as_succeeded(ml_trie_node::in) is det.

    % Set the status of the given call to incomplete.
    %
:- impure pred table_memo_mark_as_incomplete(memo_non_record::in) is det.

    % Set the status of the given call to active, and fail.
    %
:- impure pred table_memo_mark_as_active_and_fail(memo_non_record::in)
    is failure.

    % Set the status of the given call to complete, and fail.
    %
:- impure pred table_memo_mark_as_complete_and_fail(memo_non_record::in)
    is failure.

    % Create an answer block with the given number of slots and add it
    % to the given table.
    %
:- impure pred table_memo_create_answer_block(ml_trie_node::in, int::in,
    ml_answer_block::out) is det.
:- impure pred table_memo_fill_answer_block_shortcut(ml_trie_node::in) is det.

    % Return the answer block for the given call.
    %
:- semipure pred table_memo_get_answer_block(ml_trie_node::in,
    ml_answer_block::out) is det.
:- semipure pred table_memo_get_answer_block_shortcut(ml_trie_node::in) is det.

    % Return the table of answers already returned to the given nondet
    % table.
    %
:- semipure pred table_memo_non_get_answer_table(memo_non_record::in,
    ml_trie_node::out) is det.

    % If the answer represented by the given answer table
    % has not been generated before by this subgoal,
    % succeed and remember the answer as having been generated.
    % If the answer has been generated before, fail.
    %
:- impure pred table_memo_non_answer_is_not_duplicate(ml_trie_node::in)
    is semidet.
:- impure pred table_memo_non_answer_is_not_duplicate_shortcut(
    memo_non_record::in) is semidet.

    % Add an answer block to the given table.
    %
:- impure pred table_memo_non_create_answer_block_shortcut(memo_non_record::in)
    is det.

    % Return all the answer blocks for the given model_non call.
    %
:- semipure pred table_memo_return_all_answers_nondet(memo_non_record::in,
    ml_answer_block::out) is nondet.
:- semipure pred table_memo_return_all_answers_multi(memo_non_record::in,
    ml_answer_block::out) is multi.
:- semipure pred table_memo_non_return_all_shortcut(memo_non_record::in)
    is det.

    % N.B. interface continued below

%---------------------------------------------------------------------------%

:- implementation.

:- type memo_non_record
    --->    memo_non_record(c_pointer).
:- pragma foreign_type("C", memo_non_record, "MR_MemoNonRecordPtr",
    [can_pass_as_mercury_type]).

:- pragma foreign_proc("C",
    table_memo_det_setup(T::in, Status::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_det_setup(MR_TABLE_DEBUG_BOOL, MR_FALSE, T, Status);
").

table_memo_det_setup(_, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_memo_det_setup").

:- pragma foreign_proc("C",
    table_memo_det_setup_shortcut(T0::in, T::out, Status::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_det_setup_shortcut(T0, T, Status);
").

table_memo_det_setup_shortcut(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_memo_det_setup_shortcut").

:- pragma foreign_proc("C",
    table_memo_semi_setup(T::in, Status::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_semi_setup(MR_TABLE_DEBUG_BOOL, MR_FALSE, T, Status);
").

table_memo_semi_setup(_, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_memo_semi_setup").

:- pragma foreign_proc("C",
    table_memo_semi_setup_shortcut(T0::in, T::out, Status::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_semi_setup_shortcut(T0, T, Status);
").

table_memo_semi_setup_shortcut(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_memo_semi_setup_shortcut").

:- pragma foreign_proc("C",
    table_memo_non_setup(T0::in, Record::out, Status::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_non_setup(MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, Record, Status);
").

:- pragma foreign_proc("C",
    table_memo_mark_as_failed(T::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_mark_as_failed(MR_TABLE_DEBUG_BOOL, T);
").

table_memo_mark_as_failed(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_memo_mark_as_failed"),
    fail.

:- pragma foreign_proc("C",
    table_memo_mark_as_succeeded(T::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_mark_as_succeeded(MR_TABLE_DEBUG_BOOL, T);
").

table_memo_mark_as_succeeded(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_memo_mark_as_succeeded").

:- pragma foreign_proc("C",
    table_memo_mark_as_incomplete(R::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_mark_as_incomplete(MR_TABLE_DEBUG_BOOL, R);
").

table_memo_mark_as_incomplete(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_memo_mark_as_incomplete").

:- pragma foreign_proc("C",
    table_memo_mark_as_active_and_fail(R::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_mark_as_active_and_fail(MR_TABLE_DEBUG_BOOL, R);
").

table_memo_mark_as_active_and_fail(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_memo_mark_as_active_and_fail").

:- pragma foreign_proc("C",
    table_memo_mark_as_complete_and_fail(R::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_mark_as_complete_and_fail(MR_TABLE_DEBUG_BOOL, R);
").

table_memo_mark_as_complete_and_fail(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_memo_mark_as_complete_and_fail").

:- pragma foreign_proc("C",
    table_memo_create_answer_block(T::in, Size::in, AnswerBlock::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_create_answer_block(MR_TABLE_DEBUG_BOOL,
        T, Size, AnswerBlock);
").

table_memo_create_answer_block(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_memo_create_answer_block").

:- pragma foreign_proc("C",
    table_memo_fill_answer_block_shortcut(T::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_fill_answer_block_shortcut(T);
").

table_memo_fill_answer_block_shortcut(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_memo_fill_answer_block_shortcut").

:- pragma foreign_proc("C",
    table_memo_get_answer_block(T::in, AnswerBlock::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_memo_get_answer_block(MR_TABLE_DEBUG_BOOL, T, AnswerBlock);
").

table_memo_get_answer_block(_, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_memo_get_answer_block").

:- pragma foreign_proc("C",
    table_memo_get_answer_block_shortcut(T::in),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_memo_get_answer_block_shortcut(T);
").

table_memo_get_answer_block_shortcut(_) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_memo_get_answer_block_shortcut").

:- pragma foreign_proc("C",
    table_memo_non_get_answer_table(R::in, AT::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_memo_non_get_answer_table(MR_TABLE_DEBUG_BOOL, R, AT);
").

table_memo_non_get_answer_table(_, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_memo_non_get_answer_table").

:- pragma foreign_proc("C",
    table_memo_non_answer_is_not_duplicate(T::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_non_answer_is_not_duplicate(MR_TABLE_DEBUG_BOOL,
        T, SUCCESS_INDICATOR);
").

table_memo_non_answer_is_not_duplicate(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_memo_non_answer_is_not_duplicate").

:- pragma foreign_proc("C",
    table_memo_non_answer_is_not_duplicate_shortcut(R::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_non_answer_is_not_duplicate_shortcut(R,
        SUCCESS_INDICATOR);
").

table_memo_non_answer_is_not_duplicate_shortcut(_) :-
    impure private_builtin.imp,
    private_builtin.sorry(
        "table_memo_non_answer_is_not_duplicate_shortcut").

:- pragma foreign_proc("C",
    table_memo_non_create_answer_block_shortcut(R::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_memo_non_create_answer_block_shortcut(R::in);
").

table_memo_non_create_answer_block_shortcut(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_memo_non_create_answer_block_shortcut").

:- pragma foreign_proc("C",
    table_memo_non_return_all_shortcut(R::in),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_memo_non_return_all_shortcut(R);
").

table_memo_non_return_all_shortcut(_) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_memo_non_return_all_shortcut").

:- pragma external_pred(table_memo_return_all_answers_nondet/2).
:- pragma external_pred(table_memo_return_all_answers_multi/2).

%---------------------------------------------------------------------------%

:- interface.

:- import_module io.

    % This procedure should be called exactly once for each I/O action.
    % If I/O tabling is enabled, this predicate will increment the I/O
    % action counter, and will check if this action should be tabled.
    % If not, it fails. If yes, it succeeds, and binds the output
    % arguments, which are, in order:
    %
    % - The root trie node for all I/O actions. This is similar to
    %   the per-procedure tabling pointers, but it is shared by all
    %   I/O actions.
    % - the I/O action number of this action.
    % - The I/O action number of the first action in the tabled range.
    %
    % After the first tabled action, the root trie node will point to a
    % (dynamically expandable) array of trie nodes. The trie node for
    % I/O action number Counter is at offset Counter - Start in this array,
    % where Start is the I/O action number of the first tabled action.
    % The three output parameters together specify this location.
    %
:- impure pred table_io_in_range(ml_trie_node::out, int::out, int::out)
    is semidet.

    % This procedure should be called exactly once for each I/O action
    % for which table_io_in_range returns true. Given the trie node
    % for a given I/O action number, it returns true iff that action has
    % been carried out before (i.e. the action is now being reexecuted
    % after a retry command in the debugger).
    %
:- impure pred table_io_has_occurred(ml_trie_node::in) is semidet.

    % This predicate simply copies the input I/O state to become the output
    % I/O state. It is used only because it is easier to get the insts
    % right by calling this procedure than by hand-writing insts for a
    % unification.
    %
:- pred table_io_copy_io_state(io.state::di, io.state::uo) is det.

    % Calls to these predicates bracket the code of foreign_procs with
    % the tabled_for_io_unitize annotation. The left bracket procedure
    % returns the current value of MR_debug_enabled, and then turns off
    % both MR_debug_enabled and MR_io_tabling_enabled. (We don't need to
    % save MR_io_tabling_enabled because we only get to this code if it
    % contains true.) The right bracket code takes the value returned by
    % the left bracket as input and restores both globals to the values
    % they had before the call to the left bracket.
    %
:- impure pred table_io_left_bracket_unitized_goal(int::out) is det.
:- impure pred table_io_right_bracket_unitized_goal(int::in) is det.

    % N.B. interface continued below

%---------------------------------------------------------------------------%

:- implementation.

% For purposes of I/O tabling, we divide the program's execution into four
% phases.
%
% Phase UNINIT consists of Mercury code executed prior to the first debugger
% event. Even if main/2 is traced, this will include the initialization of the
% I/O system itself. During this phase, MR_io_tabling_enabled will be MR_FALSE.
%
% Phase BEFORE consists of Mercury code during whose execution the user does
% not need safe retry across I/O, probably because he/she does not require
% retry at all. During this phase, MR_io_tabling_enabled will be MR_TRUE while
% we ensure that table_io_range returns MR_FALSE by setting MR_io_tabling_start
% to the highest possible value.
%
% Phase DURING consists of Mercury code during whose execution the user does
% need safe retry across I/O. During this phase, MR_io_tabling_enabled will be
% MR_TRUE, and MR_io_tabling_start will be set to the value of
% MR_io_tabling_counter on entry to phase DURING. We will ensure that
% table_io_in_range returns MR_TRUE by setting MR_io_tabling_end to the highest
% possible value.
%
% Phase AFTER again consists of Mercury code during whose execution the user
% does not need safe retry across I/O. During this phase, MR_io_tabling_enabled
% will be MR_TRUE, MR_io_tabling_start will contain the value of
% MR_io_tabling_counter at the time of the entry to phase DURING, while
% MR_io_tabling_end will contain the value of MR_io_tabling_counter at the end
% of phase DURING, thus ensuring that table_io_in_range again returns MR_FALSE.
%
% The transition from phase UNINIT to phase BEFORE will occur during the
% initialization of the debugger, at the first trace event.
%
% The transition from phase BEFORE to phase DURING will occur when the user
% issues the "table_io start" command, while the transition from phase DURING
% to phase AFTER will occur when the user issues the "table_io end" command.
% The user may automate entry into phase DURING by putting "table_io start"
% into a .mdbrc file. Of course the program will never enter phase DURING or
% phase AFTER if the user never gives the commands that start those phases.
%
% The debugger itself invokes Mercury code e.g. to print the values of
% variables. During such calls it will set MR_io_tabling_enabled to MR_FALSE,
% since the I/O actions executed during such times do not belong to the user
% program.

:- pragma foreign_decl("C", "
    #include ""mercury_tabling.h""      // for MR_copy_table_steps
    #include ""mercury_trace_base.h""   // for MR_io_tabling_*
").

:- pragma foreign_proc("C",
    table_io_in_range(T::out, Counter::out, Start::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_io_in_range(MR_TABLE_DEBUG_BOOL, T, Counter, Start,
        SUCCESS_INDICATOR);
").

table_io_in_range(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_io_in_range").

:- pragma foreign_proc("C",
    table_io_has_occurred(T::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_io_has_occurred(MR_TABLE_DEBUG_BOOL, T, SUCCESS_INDICATOR);
").

table_io_has_occurred(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_io_has_occurred").

table_io_copy_io_state(IO, IO).

:- pragma foreign_proc("C",
    table_io_left_bracket_unitized_goal(TraceEnabled::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_io_left_bracket_unitized_goal(TraceEnabled);
").

table_io_left_bracket_unitized_goal(_TraceEnabled) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_io_left_bracket_unitized_goal").

:- pragma foreign_proc("C",
    table_io_right_bracket_unitized_goal(TraceEnabled::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_io_right_bracket_unitized_goal(TraceEnabled);
").

table_io_right_bracket_unitized_goal(_TraceEnabled) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_io_right_bracket_unitized_goal").

%---------------------------------------------------------------------------%

:- interface.

%
% Predicates that manage the stack copy model of minimal model tabling.
%

    % This type should correspond exactly to the type MR_SubgoalStatus
    % defined in runtime/mercury_tabling.h.
    %
:- type mm_status
    --->    mm_inactive
    ;       mm_active
    ;       mm_complete.

    % This type represents the data structure at the tips of the call table
    % in the stack copy implementation of minimal model tabling.
    %
:- type ml_subgoal.

    % Check if this minimal_model subgoal has been called before.
    % If not, return inactive as the old status, set up the subgoal's
    % structure, and mark it as active, since the caller is about to become
    % the subgoal's generator. If yes, return the status currently recorded
    % in the subgoal structure.
    %
:- impure pred table_mm_setup(ml_trie_node::in, ml_subgoal::out,
    mm_status::out) is det.

    % Save the state of the current subgoal and fail. Sometime later,
    % when the subgoal has some solutions, table_mm_completion will
    % restore the saved state. At the time, table_mm_suspend_consumer
    % will succeed, and return an answer block as its second argument.
    %
:- impure pred table_mm_suspend_consumer(ml_subgoal::in, ml_answer_block::out)
    is nondet.

    % Resume all suspended subgoal calls. This predicate will resume each
    % of the suspended subgoals that depend on it in turn until it reaches
    % a fixed point, at which all depended suspended subgoals have had
    % all available answers returned to them.
    %
:- impure pred table_mm_completion(ml_subgoal::in) is det.

    % Return the table of answers already returned to the given nondet
    % table.
    %
:- semipure pred table_mm_get_answer_table(ml_subgoal::in, ml_trie_node::out)
    is det.

    % If the answer represented by the given answer table
    % has not been generated before by this subgoal,
    % succeed and remember the answer as having been generated.
    % If the answer has been generated before, fail.
    %
:- impure pred table_mm_answer_is_not_duplicate(ml_trie_node::in) is semidet.
:- impure pred table_mm_answer_is_not_duplicate_shortcut(ml_subgoal::in)
    is semidet.

    % Create a new slot in the answer list of the subgoal, create a new
    % answer block of the given size, and put the answer block in the new
    % slot.
    %
:- impure pred table_mm_create_answer_block(ml_subgoal::in, int::in,
    ml_answer_block::out) is det.

:- impure pred table_mm_fill_answer_block_shortcut(ml_subgoal::in) is det.

    % Return all of the answer blocks stored in the given table.
    %
:- semipure pred table_mm_return_all_nondet(ml_subgoal::in,
    ml_answer_block::out) is nondet.
:- semipure pred table_mm_return_all_multi(ml_subgoal::in,
    ml_answer_block::out) is multi.
:- semipure pred table_mm_return_all_shortcut(ml_answer_block::in) is det.

    % N.B. interface continued below

%---------------------------------------------------------------------------%

:- implementation.

:- type ml_subgoal
    --->    ml_subgoal(c_pointer).
:- pragma foreign_type("C", ml_subgoal, "MR_SubgoalPtr",
    [can_pass_as_mercury_type]).

    % This type represents a list of answers of a model_non predicate.
    %
:- type ml_answer_list
    --->    ml_answer_list(c_pointer).
:- pragma foreign_type("C", ml_answer_list, "MR_AnswerList",
    [can_pass_as_mercury_type]).

:- pragma foreign_proc("C",
    table_mm_setup(T::in, Subgoal::out, Status::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_mm_setup(MR_TABLE_DEBUG_BOOL, MR_FALSE, T, Subgoal, Status);
").

    % The definitions of these two predicates are in the runtime system,
    % in runtime/mercury_minimal_model.c.
    %
:- pragma external_pred(table_mm_suspend_consumer/2).
:- pragma external_pred(table_mm_completion/1).
:- pragma external_pred(table_mm_return_all_nondet/2).
:- pragma external_pred(table_mm_return_all_multi/2).

:- pragma foreign_proc("C",
    table_mm_get_answer_table(Subgoal::in, AnswerTable::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_mm_get_answer_table(MR_TABLE_DEBUG_BOOL, Subgoal, AnswerTable);
").

table_mm_get_answer_table(_, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_mm_get_answer_table").

:- pragma foreign_proc("C",
    table_mm_answer_is_not_duplicate(TrieNode::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_mm_answer_is_not_duplicate(MR_TABLE_DEBUG_BOOL, TrieNode,
        SUCCESS_INDICATOR);
").

table_mm_answer_is_not_duplicate(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_mm_answer_is_not_duplicate").

:- pragma foreign_proc("C",
    table_mm_answer_is_not_duplicate_shortcut(Subgoal::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    // The body of this predicate doesn't matter, because it will never be
    // referred to. When the compiler creates references to this predicate,
    // it always overrides the predicate body.

    // mention Subgoal to shut up the warning.
    MR_fatal_error(""table_mm_answer_is_not_duplicate_shortcut: direct call"");
").

table_mm_answer_is_not_duplicate_shortcut(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_mm_answer_is_not_duplicate_shortcut").

:- pragma foreign_proc("C",
    table_mm_create_answer_block(Subgoal::in, Size::in, AnswerBlock::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_mm_create_answer_block(MR_TABLE_DEBUG_BOOL,
        Subgoal, Size, AnswerBlock);
").

table_mm_create_answer_block(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_mm_create_answer_block").

:- pragma foreign_proc("C",
    table_mm_fill_answer_block_shortcut(Subgoal::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_mm_fill_answer_block_shortcut(Subgoal);
").

table_mm_fill_answer_block_shortcut(_) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_mm_fill_answer_block_shortcut").

:- pragma foreign_proc("C",
    table_mm_return_all_shortcut(AnswerBlock::in),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_mm_return_all_shortcut(AnswerBlock);
").

table_mm_return_all_shortcut(_) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_mm_return_all_shortcut").

%---------------------------------------------------------------------------%

:- interface.

%
% Predicates that manage the own stack model of minimal model tabling.
%

    % This type represents the data structure at the tips of the call table
    % in the own stack implementation of minimal model tabling.
    %
:- type ml_consumer.

    % This type represents the generators in the own stack implementation
    % of minimal model tabling.
    %
:- type ml_generator.

    % Save the information that will be needed later about this subgoal
    % in a temporary data structure in the runtime.
    %
:- impure pred table_mmos_save_inputs is det.

    % The given trie node should be at the tip of a call table,
    % indicating a subgoal. Create a generator for this subgoal if
    % necessary (if the trie node indicates it has not been called before),
    % and then register this call as a consumer of the answers of the
    % generator.
    %
:- impure pred table_mmos_setup_consumer(ml_trie_node::in, c_pointer::in,
    ml_consumer::out) is det.

    % If the answer represented by the given answer table
    % has not been generated before by this subgoal,
    % succeed and remember the answer as having been generated.
    % If the answer has been generated before, fail.
    %
:- impure pred table_mmos_answer_is_not_duplicate(ml_trie_node::in) is semidet.

    % This is a version of table_mmos_answer_is_not_duplicate
    % in intended for bundling with other operations.
    %
:- impure pred table_mmos_answer_is_not_duplicate_shortcut(ml_generator::in)
    is semidet.

    % This predicate checks whether there is a next answer already listed
    % for the consumer. If yes, it returns it. If not, it wakes up the
    % generator to create more answers if possible.
    %
:- impure pred table_mmos_consume_next_answer_nondet(ml_consumer::in,
    ml_answer_block::out) is nondet.
:- impure pred table_mmos_consume_next_answer_multi(ml_consumer::in,
    ml_answer_block::out) is multi.

    % Return all the answers of a consumers, in extra arguments hung
    % on a foreign_proc invocation of this predicate.
    %
:- semipure pred table_mmos_restore_answers(ml_answer_block::in) is det.

    % Pickup the input arguments of the generator from where the consumer
    % put them.
    %
:- impure pred table_mmos_pickup_inputs(ml_generator::out) is det.

    % Create an answer block of the given size for a new answer of the
    % given generator.
    %
:- impure pred table_mmos_create_answer_block(ml_generator::in,
    int::in, ml_answer_block::out) is det.

    % Return the given answer to the consumer(s) that requested it.
    %
:- impure pred table_mmos_return_answer(ml_generator::in, ml_answer_block::in)
    is det.

    % Resume all suspended subgoal calls. This predicate will resume each
    % of the suspended subgoals that depend on it in turn until it reaches
    % a fixed point, at which all depended suspended subgoals have had
    % all available answers returned to them.
    %
:- impure pred table_mmos_completion(ml_generator::in) is failure.

:- implementation.

    % This type represents the data structure at the tips of the call table
    % in the own stack implementation of minimal model tabling.
    %
:- type ml_consumer
    --->    ml_consumer(c_pointer).
:- pragma foreign_type("C", ml_consumer, "MR_ConsumerPtr",
    [can_pass_as_mercury_type]).

    % This type represents the generators in the own stack implementation
    % of minimal model tabling.
    %
:- type ml_generator
    --->    ml_generator(c_pointer).
:- pragma foreign_type("C", ml_generator, "MR_GeneratorPtr",
    [can_pass_as_mercury_type]).

:- pragma foreign_proc("C",
    table_mmos_save_inputs,
    [will_not_call_mercury, does_not_affect_liveness],
"
    // The body of this predicate doesn't matter, because it will never be
    // referred to. When the compiler creates references to this predicate,
    // it always overrides the predicate body.
    MR_fatal_error(""table_mmos_save_inputs: direct call"");
").

table_mmos_save_inputs :-
    impure private_builtin.imp.

:- pragma foreign_proc("C",
    table_mmos_setup_consumer(T::in, GeneratorPred::in, Consumer::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    // The body of this predicate doesn't matter, because it will never be
    // referred to. When the compiler creates references to this predicate,
    // it always overrides the predicate body.

    // Mention T, GeneratorPred, Consumer to shut up the warning.
    MR_fatal_error(""table_mmos_setup_consumer: direct call"");
").

table_mmos_setup_consumer(_, _, Consumer) :-
    impure private_builtin.imp,
    % Required only to avoid warnings; never executed.
    pretend_to_generate_value(Consumer).

    % Required only to avoid warnings; never executed.
    %
:- pred pretend_to_generate_value(T::out) is det.
:- pragma consider_used(pred(pretend_to_generate_value/1)).

:- pragma foreign_proc("C",
    table_mmos_answer_is_not_duplicate(T::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    // The body of this predicate doesn't matter, because it will never be
    // referred to. When the compiler creates references to this predicate,
    // it always overrides the predicate body.

    // Mention T to shut up the warning.
    MR_fatal_error(""table_mmos_answer_is_not_duplicate: direct call"");
").

:- pragma foreign_proc("C",
    table_mmos_answer_is_not_duplicate_shortcut(G::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    // The body of this predicate doesn't matter, because it will never be
    // referred to. When the compiler creates references to this predicate,
    // it always overrides the predicate body.

    // Mention G to shut up the warning.
    MR_fatal_error(
        ""table_mmos_answer_is_not_duplicate_shortcut: direct call"");
").

:- pragma external_pred(table_mmos_consume_next_answer_nondet/2).
:- pragma external_pred(table_mmos_consume_next_answer_multi/2).

:- pragma foreign_proc("C",
    table_mmos_restore_answers(AnswerBlock::in),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    // The body of this predicate doesn't matter, because it will never be
    // referred to. When the compiler creates references to this predicate,
    // it always overrides the predicate body.

    // Mention AnswerBlock to shut up the warning.
    MR_fatal_error(""table_mmos_restore_answers: direct call"");
").

:- pragma foreign_proc("C",
    table_mmos_pickup_inputs(Generator::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    // The body of this predicate doesn't matter, because it will never be
    // referred to. When the compiler creates references to this predicate,
    // it always overrides the predicate body.

    // mention Generator to shut up the warning.
    MR_fatal_error(""table_mmos_pickup_inputs: direct call"");
").

:- pragma foreign_proc("C",
    table_mmos_create_answer_block(Generator::in, BlockSize::in,
        AnswerBlock::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    // MR_tbl_mmos_create_answer_block(Generator, BlockSize, AnswerBlock);
").

table_mmos_create_answer_block(_, _, AnswerBlock) :-
    impure private_builtin.imp,
    % Required only to avoid warnings; never executed.
    pretend_to_generate_value(AnswerBlock).

:- pragma foreign_proc("C",
    table_mmos_return_answer(Generator::in, AnswerBlock::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    // MR_tbl_mmos_return_answer(Generator, AnswerBlock);
").

table_mmos_return_answer(_, _) :-
    impure private_builtin.imp.

:- pragma foreign_proc("C",
    table_mmos_completion(Generator::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    // MR_tbl_mmos_completion(Generator);
").

table_mmos_completion(_) :-
    impure private_builtin.imp,
    fail.

pretend_to_generate_value(Bogus) :-
    % The following code will throw an exception if executed.
    det_univ_to_type(univ(0), Bogus).

%---------------------------------------------------------------------------%

:- interface.

%
% Utility predicates that are needed in the tabling of all kinds of subgoals.
%

%
% The following table_lookup_insert... predicates lookup or insert the second
% argument into the trie pointed to by the first argument. The value returned
% is a pointer to the leaf of the trie reached by the lookup. From the
% returned leaf another trie may be connected.
%

    % Lookup or insert an integer in the given table.
    %
:- impure pred table_lookup_insert_int(ml_trie_node::in, int::in,
    ml_trie_node::out) is det.

    % Lookup or insert an unsigned integer in the given table.
    %
:- impure pred table_lookup_insert_uint(ml_trie_node::in, uint::in,
    ml_trie_node::out) is det.

    % Lookup or insert a signed 8-bit integer in the given table.
    %
:- impure pred table_lookup_insert_int8(ml_trie_node::in, int8::in,
    ml_trie_node::out) is det.

    % Lookup or insert an unsigned 8-bit integer in the given table.
    %
:- impure pred table_lookup_insert_uint8(ml_trie_node::in, uint8::in,
    ml_trie_node::out) is det.

    % Lookup or insert a signed 16-bit integer in the given table.
    %
:- impure pred table_lookup_insert_int16(ml_trie_node::in, int16::in,
    ml_trie_node::out) is det.

    % Lookup or insert an unsigned 16-bit integer in the given table.
    %
:- impure pred table_lookup_insert_uint16(ml_trie_node::in, uint16::in,
    ml_trie_node::out) is det.

    % Lookup or insert a signed 32-bit integer in the given table.
    %
:- impure pred table_lookup_insert_int32(ml_trie_node::in, int32::in,
    ml_trie_node::out) is det.

    % Lookup or insert an unsigned 32-bit integer in the given table.
    %
:- impure pred table_lookup_insert_uint32(ml_trie_node::in, uint32::in,
    ml_trie_node::out) is det.

    % Lookup or insert a signed 64-bit integer in the given table.
    %
:- impure pred table_lookup_insert_int64(ml_trie_node::in, int64::in,
    ml_trie_node::out) is det.

    % Lookup or insert an unsigned 64-bit integer in the given table.
    %
:- impure pred table_lookup_insert_uint64(ml_trie_node::in, uint64::in,
    ml_trie_node::out) is det.

    % Lookup or insert an integer in the given table.
    %
:- impure pred table_lookup_insert_start_int(ml_trie_node::in, int::in,
    int::in, ml_trie_node::out) is det.

    % Lookup or insert a character in the given trie.
    %
:- impure pred table_lookup_insert_char(ml_trie_node::in, character::in,
    ml_trie_node::out) is det.

    % Lookup or insert a string in the given trie.
    %
:- impure pred table_lookup_insert_string(ml_trie_node::in, string::in,
    ml_trie_node::out) is det.

    % Lookup or insert a float in the current trie.
    %
:- impure pred table_lookup_insert_float(ml_trie_node::in, float::in,
    ml_trie_node::out) is det.

    % Lookup or insert an enumeration type in the given trie.
    %
:- impure pred table_lookup_insert_enum(ml_trie_node::in, int::in, T::in,
    ml_trie_node::out) is det.

    % Lookup or insert a foreign enumeration type in the given trie.
    %
:- impure pred table_lookup_insert_foreign_enum(ml_trie_node::in, T::in,
    ml_trie_node::out) is det.

    % Lookup or insert a monomorphic general type in the given trie.
    %
:- impure pred table_lookup_insert_gen(ml_trie_node::in, T::in,
    ml_trie_node::out) is det.

    % Lookup or insert a monomorphic general type in the given trie,
    % tabling terms without traversing them. This makes the operation fast,
    % but if a term was inserted previously, we will catch it only if the
    % insert was the exact same memory cells. (This is the "loose" part.)
    %
:- impure pred table_lookup_insert_gen_addr(ml_trie_node::in, T::in,
    ml_trie_node::out) is det.

    % Lookup or insert a polymorphic general type in the given trie.
    %
:- impure pred table_lookup_insert_gen_poly(ml_trie_node::in, T::in,
    ml_trie_node::out) is det.

    % Lookup or insert a polymorphic general type in the given trie,
    % tabling terms without traversing them. This makes the operation fast,
    % but if a term was inserted previously, we will catch it only if the
    % insert was the exact same memory cells. (This is the "loose" part.)
    %
:- impure pred table_lookup_insert_gen_poly_addr(ml_trie_node::in, T::in,
    ml_trie_node::out) is det.

    % Lookup or insert a type_info in the given trie.
    %
:- impure pred table_lookup_insert_typeinfo(ml_trie_node::in,
    private_builtin.type_info::in, ml_trie_node::out) is det.

    % Lookup or insert a typeclass_info in the given trie.
    %
:- impure pred table_lookup_insert_typeclassinfo(ml_trie_node::in,
    private_builtin.typeclass_info::in, ml_trie_node::out) is det.

    % Save an integer answer in the given answer block at the given
    % offset.
    %
:- impure pred table_save_int_answer(ml_answer_block::in, int::in, int::in)
    is det.

    % Save an unsigned integer answer in the given answer block at the given
    % offset.
    %
:- impure pred table_save_uint_answer(ml_answer_block::in, int::in, uint::in)
    is det.

    % Save a signed 8-bit integer answer in the given answer block at the
    % given offset.
    %
:- impure pred table_save_int8_answer(ml_answer_block::in, int::in, int8::in)
    is det.

    % Save an unsigned 8-bit integer answer in the given answer block at the
    % given offset.
    %
:- impure pred table_save_uint8_answer(ml_answer_block::in, int::in, uint8::in)
    is det.

    % Save a signed 16-bit integer answer in the given answer block at the
    % given offset.
    %
:- impure pred table_save_int16_answer(ml_answer_block::in, int::in, int16::in)
    is det.

    % Save an unsigned 16-bit integer answer in the given answer block at the
    % given offset.
    %
:- impure pred table_save_uint16_answer(ml_answer_block::in, int::in,
    uint16::in) is det.

    % Save a signed 32-bit integer answer in the given answer block at the
    % given offset.
    %
:- impure pred table_save_int32_answer(ml_answer_block::in, int::in, int32::in)
    is det.

    % Save an unsigned 32-bit integer answer in the given answer block at the
    % given offset.
    %
:- impure pred table_save_uint32_answer(ml_answer_block::in, int::in,
    uint32::in) is det.

    % Save a signed 64-bit integer answer in the given answer block at the
    % given offset.
    %
:- impure pred table_save_int64_answer(ml_answer_block::in, int::in, int64::in)
    is det.

    % Save an unsigned 64-bit integer answer in the given answer block at the
    % given offset.
    %
:- impure pred table_save_uint64_answer(ml_answer_block::in, int::in,
    uint64::in) is det.

    % Save a character answer in the given answer block at the given
    % offset.
    %
:- impure pred table_save_char_answer(ml_answer_block::in, int::in,
    character::in) is det.

    % Save a string answer in the given answer block at the given
    % offset.
    %
:- impure pred table_save_string_answer(ml_answer_block::in, int::in,
    string::in) is det.

    % Save a float answer in the given answer block at the given
    % offset.
    %
:- impure pred table_save_float_answer(ml_answer_block::in, int::in, float::in)
    is det.

    % Save an I/O state in the given answer block at the given offset.
    %
:- impure pred table_save_io_state_answer(ml_answer_block::in, int::in,
    io.state::ui) is det.

    % Save any type of answer in the given answer block at the given
    % offset.
    %
:- impure pred table_save_any_answer(ml_answer_block::in, int::in, T::in)
    is det.

    % Restore an integer answer from the given answer block at the
    % given offset.
    %
:- semipure pred table_restore_int_answer(ml_answer_block::in, int::in,
    int::out) is det.

    % Restore an unsigned integer answer from the given answer block at
    % the given offset.
    %
:- semipure pred table_restore_uint_answer(ml_answer_block::in, int::in,
    uint::out) is det.

    % Restore a signed 8-bit integer answer from the given answer block at the
    % given offset.
    %
:- semipure pred table_restore_int8_answer(ml_answer_block::in, int::in,
    int8::out) is det.

    % Restore an unsigned 8-bit integer answer from the given answer block at
    % the given offset.
    %
:- semipure pred table_restore_uint8_answer(ml_answer_block::in, int::in,
    uint8::out) is det.

    % Restore a signed 16-bit integer answer from the given answer block at the
    % given offset.
    %
:- semipure pred table_restore_int16_answer(ml_answer_block::in, int::in,
    int16::out) is det.

    % Restore an unsigned 16-bit integer answer from the given answer block at
    % the given offset.
    %
:- semipure pred table_restore_uint16_answer(ml_answer_block::in, int::in,
    uint16::out) is det.

    % Restore a signed 32-bit integer answer from the given answer block at the
    % given offset.
    %
:- semipure pred table_restore_int32_answer(ml_answer_block::in, int::in,
    int32::out) is det.

    % Restore an unsigned 32-bit integer answer from the given answer block at
    % the given offset.
    %
:- semipure pred table_restore_uint32_answer(ml_answer_block::in, int::in,
    uint32::out) is det.

    % Restore a signed 64-bit integer answer from the given answer block at the
    % given offset.
    %
:- semipure pred table_restore_int64_answer(ml_answer_block::in, int::in,
    int64::out) is det.

    % Restore an unsigned 64-bit integer answer from the given answer block at
    % the given offset.
    %
:- semipure pred table_restore_uint64_answer(ml_answer_block::in, int::in,
    uint64::out) is det.

    % Restore a character answer from the given answer block at the
    % given offset.
    %
:- semipure pred table_restore_char_answer(ml_answer_block::in, int::in,
    character::out) is det.

    % Restore a string answer from the given answer block at the
    % given offset.
    %
:- semipure pred table_restore_string_answer(ml_answer_block::in, int::in,
    string::out) is det.

    % Restore a float answer from the given answer block at the
    % given offset.
    %
:- semipure pred table_restore_float_answer(ml_answer_block::in, int::in,
    float::out) is det.

    % Restore an I/O state from the given answer block at the given offset.
    %
:- semipure pred table_restore_io_state_answer(ml_answer_block::in, int::in,
    io.state::uo) is det.

    % Restore any type of answer from the given answer block at the
    % given offset.
    %
:- semipure pred table_restore_any_answer(ml_answer_block::in, int::in, T::out)
    is det.

    % Report an error message.
    %
:- pred table_error(string::in) is erroneous.

    % Report statistics on the operation of the tabling system to stderr.
    %
:- impure pred table_report_statistics is det.

%---------------------------------------------------------------------------%

:- implementation.

:- import_module require.
:- import_module univ.

:- pragma foreign_decl("C", "

#include ""mercury_misc.h""         // for MR_fatal_error()
#include ""mercury_type_info.h""    // for MR_TypeCtorInfo_Struct
#include ""mercury_tabling.h""      // for MR_TrieNode, etc.

MR_DECLARE_TYPE_CTOR_INFO_STRUCT(MR_TYPE_CTOR_INFO_NAME(io, state, 0));

").

%---------------------------------------------------------------------------%

:- pragma foreign_proc("C",
    table_lookup_insert_int(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_int(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
").

table_lookup_insert_int(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_int").

:- pragma foreign_proc("C",
    table_lookup_insert_uint(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_uint(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
").

table_lookup_insert_uint(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_uint").

:- pragma foreign_proc("C",
    table_lookup_insert_int8(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_int8(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
").

table_lookup_insert_int8(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_int8").

:- pragma foreign_proc("C",
    table_lookup_insert_uint8(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_uint8(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
").

table_lookup_insert_uint8(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_uint8").

:- pragma foreign_proc("C",
    table_lookup_insert_int16(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_int16(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
").

table_lookup_insert_int16(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_int16").

:- pragma foreign_proc("C",
    table_lookup_insert_uint16(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_uint16(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
").

table_lookup_insert_uint16(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_uint16").

:- pragma foreign_proc("C",
    table_lookup_insert_int32(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_int32(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
").

table_lookup_insert_int32(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_int32").

:- pragma foreign_proc("C",
    table_lookup_insert_uint32(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_uint32(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
").

table_lookup_insert_uint32(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_uint32").

:- pragma foreign_proc("C",
    table_lookup_insert_int64(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_int64(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
").

table_lookup_insert_int64(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_int64").

:- pragma foreign_proc("C",
    table_lookup_insert_uint64(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_uint64(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
").

table_lookup_insert_uint64(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_uint64").

:- pragma foreign_proc("C",
    table_lookup_insert_start_int(T0::in, S::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_start_int(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE,
        T0, S, V, T);
").

table_lookup_insert_start_int(_, _, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_start_int").

:- pragma foreign_proc("C",
    table_lookup_insert_char(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_char(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
").

table_lookup_insert_char(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_char").

:- pragma foreign_proc("C",
    table_lookup_insert_string(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_string(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
").

table_lookup_insert_string(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_string").

:- pragma foreign_proc("C",
    table_lookup_insert_float(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_float(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
").

table_lookup_insert_float(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_float").

:- pragma foreign_proc("C",
    table_lookup_insert_enum(T0::in, R::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_enum(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0,
        R, V, T);
").

table_lookup_insert_enum(_, _, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_enum").

:- pragma foreign_proc("C",
    table_lookup_insert_foreign_enum(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_foreign_enum(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0,
        V, T);
").

:- pragma foreign_proc("C",
    table_lookup_insert_gen(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_gen(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0,
        TypeInfo_for_T, V, T);
").

table_lookup_insert_gen(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_gen").

:- pragma foreign_proc("C",
    table_lookup_insert_gen_addr(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_gen_addr(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0,
        TypeInfo_for_T, V, T);
").

table_lookup_insert_gen_addr(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_gen_addr").

:- pragma foreign_proc("C",
    table_lookup_insert_gen_poly(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_gen_poly(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0,
        TypeInfo_for_T, V, T);
").

table_lookup_insert_gen_poly(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_gen_poly").

:- pragma foreign_proc("C",
    table_lookup_insert_gen_poly_addr(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_gen_poly_addr(NULL, MR_TABLE_DEBUG_BOOL,
        MR_FALSE, T0, TypeInfo_for_T, V, T);
").

table_lookup_insert_gen_poly_addr(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_lookup_insert_gen_poly_addr").

:- pragma foreign_proc("C",
    table_lookup_insert_typeinfo(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_typeinfo(NULL, MR_TABLE_DEBUG_BOOL,
        MR_FALSE, T0, V, T);
").

:- pragma foreign_proc("C",
    table_lookup_insert_typeclassinfo(T0::in, V::in, T::out),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_lookup_insert_typeclassinfo(NULL, MR_TABLE_DEBUG_BOOL,
        MR_FALSE, T0, V, T);
").

%---------------------------------------------------------------------------%

:- pragma foreign_proc("C",
    table_save_int_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_int_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_int_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_int_answer").

:- pragma foreign_proc("C",
    table_save_uint_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_uint_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_uint_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_uint_answer").

:- pragma foreign_proc("C",
    table_save_int8_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_int8_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_int8_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_int8_answer").

:- pragma foreign_proc("C",
    table_save_uint8_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_uint8_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_uint8_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_uint8_answer").

:- pragma foreign_proc("C",
    table_save_int16_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_int16_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_int16_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_int16_answer").

:- pragma foreign_proc("C",
    table_save_uint16_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_uint16_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_uint16_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_uint16_answer").

:- pragma foreign_proc("C",
    table_save_int32_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_int32_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_int32_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_int32_answer").

:- pragma foreign_proc("C",
    table_save_uint32_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_uint32_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_uint32_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_uint32_answer").

:- pragma foreign_proc("C",
    table_save_int64_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_int64_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_int64_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_int64_answer").

:- pragma foreign_proc("C",
    table_save_uint64_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_uint64_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_uint64_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_uint64_answer").

:- pragma foreign_proc("C",
    table_save_char_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_char_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_char_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_char_answer").

:- pragma foreign_proc("C",
    table_save_string_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_string_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_string_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_string_answer").

:- pragma foreign_proc("C",
    table_save_float_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_float_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_float_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_float_answer").

:- pragma foreign_proc("C",
    table_save_io_state_answer(AB::in, Offset::in, V::ui),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_io_state_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_save_io_state_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_io_state_answer").

:- pragma foreign_proc("C",
    table_save_any_answer(AB::in, Offset::in, V::in),
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_tbl_save_any_answer(MR_TABLE_DEBUG_BOOL, AB, Offset,
        TypeInfo_for_T, V);
").

table_save_any_answer(_, _, _) :-
    impure private_builtin.imp,
    private_builtin.sorry("table_save_any_answer").

:- pragma foreign_proc("C",
    table_restore_int_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_int_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_int_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_int_answer").

:- pragma foreign_proc("C",
    table_restore_uint_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_uint_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_uint_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_uint_answer").

:- pragma foreign_proc("C",
    table_restore_int8_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_int8_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_int8_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_int8_answer").

:- pragma foreign_proc("C",
    table_restore_uint8_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_uint8_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_uint8_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_uint8_answer").

:- pragma foreign_proc("C",
    table_restore_int16_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_int16_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_int16_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_int16_answer").

:- pragma foreign_proc("C",
    table_restore_uint16_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_uint16_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_uint16_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_uint16_answer").

:- pragma foreign_proc("C",
    table_restore_int32_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_int32_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_int32_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_int32_answer").

:- pragma foreign_proc("C",
    table_restore_uint32_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_uint32_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_uint32_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_uint32_answer").

:- pragma foreign_proc("C",
    table_restore_int64_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_int64_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_int64_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_int64_answer").

:- pragma foreign_proc("C",
    table_restore_uint64_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_uint64_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_uint64_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_uint64_answer").

:- pragma foreign_proc("C",
    table_restore_char_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_char_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_char_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_char_answer").

:- pragma foreign_proc("C",
    table_restore_string_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_string_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_string_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_string_answer").

:- pragma foreign_proc("C",
    table_restore_float_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_float_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_float_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_float_answer").

:- pragma foreign_proc("C",
    table_restore_io_state_answer(AB::in, Offset::in, V::uo),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_io_state_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_io_state_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_io_state_answer").

:- pragma foreign_proc("C",
    table_restore_any_answer(AB::in, Offset::in, V::out),
    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
"
    MR_tbl_restore_any_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
").

table_restore_any_answer(_, _, _) :-
    semipure private_builtin.semip,
    private_builtin.sorry("table_restore_any_answer").

table_error(Message) :-
    error(Message).

:- pragma foreign_proc("C",
    table_report_statistics,
    [will_not_call_mercury, does_not_affect_liveness],
"
    MR_table_report_statistics(stderr);
").

table_report_statistics :-
    impure private_builtin.imp,
    private_builtin.sorry("table_report_statistics").

%---------------------------------------------------------------------------%
:- end_module table_builtin.
%---------------------------------------------------------------------------%
