Files
mercury/library/array.m
Zoltan Somogyi f007b45df8 Implement the infrastructure for term size profiling.
Estimated hours taken: 400
Branches: main

Implement the infrastructure for term size profiling. This means adding two
new grade components, tsw and tsc, and implementing them in the LLDS code
generator. In grades including tsw (term size words), each term is augmented
with an extra word giving the number of heap words it contains; in grades
including tsc (term size cells), each term is augmented with an extra word
giving the number of heap cells it contains. The extra word is at the start,
at offset -1, to leave almost all of the machinery for accessing the heap
unchanged.

For now, the only way to access term sizes is with a new mdb command,
"term_size <varspec>". Later, we will use term sizes in conjunction with
deep profiling to do experimental complexity analysis, but that requires
a lot more research. This diff is a necessary first step.

The implementation of term size profiling consists of three main parts:

- a source-to-source transform that computes the size of each heap cell
  when it is constructed (and increments it in the rare cases when a free
  argument of an existing heap cell is bound),

- a relatively small change to the code generator that reserves the extra
  slot in new heap cells, and

- extensions to the facilities for creating cells from C code to record
  the extra information we now need.

The diff overhauls polymorphism.m to make the source-to-source transform
possible. This overhaul includes separating type_ctor_infos and type_infos
as strictly as possible from each other, converting type_ctor_infos into
type_infos only as necessary. It also includes separating type_ctor_infos,
type_infos, base_typeclass_infos and typeclass_infos (as well as voids,
for clarity) from plain user-defined type constructors in type categorizations.
This change needs this separation because values of those four types do not
have size slots, but they ought to be treated specially in other situations
as well (e.g. by tabling).

The diff adds a new mdb command, term_size. It also replaces the proc_body
mdb command with new ways of using the existing print and browse commands
("print proc_body" and "browse proc_body") in order to make looking at
procedure bodies more controllable. This was useful in debugging the effect
of term size profiling on some test case outputs. It is not strictly tied
to term size profiling, but turns out to be difficult to disentangle.

compiler/size_prof.m:
	A new module implementing the source-to-source transform.

compiler/notes/compiler_design.html:
	Mention the new module.

compiler/transform_hlds.m:
	Include size_prof as a submodule of transform_hlds.

compiler/mercury_compile.m:
	If term size profiling is enabled, invoke its source-to-source
	transform.

compiler/hlds_goal.m:
	Extend construction unifications with an optional slot for recording
	the size of the term if the size is a constant, or the identity of the
	variable holding the size, if the size is not constant. This is
	needed by the source-to-source transform.

compiler/quantification.m:
	Treat the variable reference that may be in this slot as a nonlocal
	variable of construction unifications, since the code generator needs
	this.

compiler/compile_target_code.m:
	Handle the new grade components.

compiler/options.m:
	Implement the options that control term size profiling.

doc/user_guide.texi:
	Document the options and grade components that control term size
	profiling, and the term_size mdb command. The documentation is
	commented out for now.

	Modify the wording of the 'u' HLDS dump flag to include other details
	of unifications (e.g. term size info) rather than just unification
	categories.

	Document the new alternatives of the print and browse commands. Since
	they are for developers only, the documentation is commented out.

compiler/handle_options.m:
	Handle the implications of term size profiling grades.

	Add a -D flag value to print HLDS components relevant to HLDS
	transformations.

compiler/modules.m:
	Import the new builtin library module that implements the operations
	needed by term size profiling automatically in term size profiling
	grades.

	Switch the predicate involved to use state var syntax.

compiler/prog_util.m:
	Add predicates and functions that return the sym_names of the modules
	needed by term size profiling.

compiler/code_info.m:
compiler/unify_gen.m:
compiler/var_locn.m:
 	Reserve an extra slot in heap cells and fill them in in unifications
	marked by size_prof.

compiler/builtin_ops.m:
	Add term_size_prof_builtin.term_size_plus as a builtin, with the same
	implementation as int.+.

compiler/make_hlds.m:
	Disable warnings about clauses for builtins while the change to
	builtin_ops is bootstrapped.

compiler/polymorphism.m:
	Export predicates that generate goals to create type_infos and
	type_ctor_infos to add_to_construct.m. Rewrite their documentation
	to make it more detailed.

	Make orders of arguments amenable to the use of state variable syntax.

	Consolidate knowledge of which type categories have builtin unify and
	compare predicates in one place.

	Add code to leave the types of type_ctor_infos alone: instead of
	changing their types to type_info when used as arguments of other
	type_infos, create a new variable of type type_info instead, and
	use an unsafe_cast. This would make the HLDS closer to being type
	correct, but this new code is currently commented out, for two
	reasons. First, common.m is currently not smart enough to figure out
	that if X and Y are equal, then similar unsafe_casts of X and Y
	are also equal, and this causes the compiler do not detect some
	duplicate calls it used to detect. Second, the code generators
	are also not smart enough to know that if Z is an unsafe_cast of X,
	then X and Z do not need separate stack slots, but can use the same
	slot.

compiler/type_util.m:
	Add utility predicates for returning the types of type_infos and
	type_ctor_infos, for use by new code in polymorphism.m.

	Move some utility predicates here from other modules, since they
	are now used by more than one module.

	Rename the type `builtin_type' as `type_category', to better reflect
	what it does. Extend it to put the type_info, type_ctor_info,
	typeclass_info, base_typeclass_info and void types into categories
	of their own: treating these types as if they were a user-defined
	type (which is how they used to be classified) is not always correct.
	Rename the functor polymorphic_type to variable_type, since types
	such as list(T) are polymorphic, but they fall into the user-defined
	category. Rename user_type as user_ctor_type, since list(int) is not
	wholly user-defined but falls into this category. Rename pred_type
	as higher_order_type, since it also encompasses functions.

	Replace code that used to check for a few of the alternatives
	of this type with code that does a full switch on the type,
	to ensure that they are updated if the type definition ever
	changes again.

compiler/pseudo_type_info.m:
	Delete a predicate whose updated implementation is now in type_util.m.

compiler/mlds_to_c.m:
compiler/mlds_to_gcc.m:
compiler/mlds_to_il.m:
compiler/mlds_to_java.m:
	Still treat type_infos, type_ctor_infos, typeclass_infos and
	base_typeclass_infos as user-defined types, but prepare for when
	they won't be.

compiler/hlds_pred.m:
	Require interface typeinfo liveness when term size profiling is
	enabled.

	Add term_size_profiling_builtin.increase_size as a
	no_type_info_builtin.

compiler/hlds_out.m:
	Print the size annotations on unifications if HLDS dump flags call
	for unification details. (The flag test is in the caller of the
	modified predicate.)

compiler/llds.m:
	Extend incr_hp instructions and data_addr_consts with optional fields
	that allow the code generator to refer to N words past the start of
	a static or dynamic cell. Term size profiling uses this with N=1.

compiler/llds_out.m:
	When allocating memory on the heap, use the macro variants that
	specify an optional offset, and specify the offset when required.

compiler/bytecode_gen.m:
compiler/dense_switch.m:
compiler/dupelim.m:
compiler/exprn_aux.m:
compiler/goal_form.m:
compiler/goal_util.m:
compiler/higher_order.m:
compiler/inst_match.m:
compiler/intermod.m:
compiler/jumpopt.m:
compiler/lambda.m:
compiler/livemap.m:
compiler/ll_pseudo_type_info.m:
compiler/lookup_switch.m:
compiler/magic_util.m:
compiler/middle_rec.m:
compiler/ml_code_util.m:
compiler/ml_switch_gen.m:
compiler/ml_unify_gen.m:
compiler/mlds.m:
compiler/mlds_to_c.m:
compiler/mlds_to_gcc.m:
compiler/mlds_to_il.m:
compiler/mlds_to_java.m:
compiler/modecheck_unify.m:
compiler/opt_debug.m:
compiler/opt_util.m:
compiler/par_conj_gen.m:
compiler/post_typecheck.m:
compiler/reassign.m:
compiler/rl.m:
compiler/rl_key.m:
compiler/special_pred.m:
compiler/stack_layout.m:
compiler/static_term.m:
compiler/string_switch.m:
compiler/switch_gen.m:
compiler/switch_util.m:
compiler/table_gen.m:
compiler/term_util.m:
compiler/type_ctor_info.m:
compiler/unused_args.m:
compiler/use_local_vars.m:
	Minor updates to conform to the changes above.

library/term_size_prof_builtin.m:
	New module containing helper predicates for term size profiling.
	size_prof.m generates call to these predicates.

library/library.m:
	Include the new module in the library.

doc/Mmakefile:
	Do not include the term_size_prof_builtin module in the library
	documentation.

library/array.m:
library/benchmarking.m:
library/construct.m:
library/deconstruct.m:
library/io.m:
library/sparse_bitset.m:
library/store.m:
library/string.m:
	Replace all uses of MR_incr_hp with MR_offset_incr_hp, to ensure
	that we haven't overlooked any places where offsets may need to be
	specified.

	Fix formatting of foreign_procs.

	Use new macros defined by the runtime system when constructing
	terms (which all happen to be lists) in C code. These new macros
	specify the types of the cell arguments, allowing the implementation
	to figure out the size of the new cell based on the sizes of its
	fields.

library/private_builtin.m:
	Define some constant type_info structures for use by these macros.
	They cannot be defined in the runtime, since they refer to types
	defined in the library (list.list and std_util.univ).

util/mkinit.c:
	Make the addresses of these type_info structures available to the
	runtime.

runtime/mercury_init.h:
	Declare these type_info structures, for use in mkinit-generated
	*_init.c files.

runtime/mercury_wrapper.[ch]:
	Declare and define the variables that hold these addresses, for use
	in the new macros for constructing typed lists.

	Since term size profiling can refer to a memory cell by a pointer
	that is offset by one word, register the extra offsets with the Boehm
	collector if is being used.

	Document the incompatibility of MR_HIGHTAGS and the Boehm collector.

runtime/mercury_tags.h:
	Define new macros for constructing typed lists.

	Provide macros for preserving the old interface presented by this file
	to the extent possible. Uses of the old MR_list_cons macro will
	continue to work in grades without term size profiling. In term
	size profiling grades, their use will get a C compiler error.

	Fix a bug caused by a missing backslash.

runtime/mercury_heap.h:
	Change the basic macros for allocating new heap cells to take
	an optional offset argument. If this is nonzero, the macros
	increment the returned address by the given number of words.
	Term size profiling specifies offset=1, reserving the extra
	word at the start (which is ignored by all components of the
	system except term size profiling) for holding the size of the term.

	Provide macros for preserving the old interface presented by this file
	to the extent possible. Since the old MR_create[123] and MR_list_cons
	macros did not specify type information, they had to be changed
	to take additional arguments. This affects only hand-written C code.

	Call new diagnostic macros that can help debug heap allocations.

	Document why the macros in this files must expand to expressions
	instead of statements, evn though the latter would be preferable
	(e.g. by allowing them to declare and use local variables without
	depending on gcc extensions).

runtime/mercury_debug.[ch]:
	Add diagnostic macros to debug heap allocations, and the functions
	behind them if MR_DEBUG_HEAP_ALLOC is defined.

	Update the debugging routines for hand-allocated cells to print the
	values of the term size slot as well as the other slots in the relevant
	grades.

runtime/mercury_string.h:
	Provide some needed variants of the macro for copying strings.

runtime/mercury_deconstruct_macros.h:
runtime/mercury_type_info.c:
	Supply type information when constructing terms.

runtime/mercury_deep_copy_body.h:
	Preserve the term size slot when copying terms.

runtime/mercury_deep_copy_body.h:
runtime/mercury_ho_call.c:
runtime/mercury_ml_expand_body.h:
	Use MR_offset_incr_hp instead of MR_incr_hp to ensure that all places
	that allocate cells also allocate space for the term size slot if
	necessary.

	Reduce code duplication by using a now standard macro for copying
	strings.

runtime/mercury_grade.h:
	Handle the two new grade components.

runtime/mercury_conf_param.h:
	Document the C macros used to control the two new grade components,
	as well as MR_DEBUG_HEAP_ALLOC.

	Detect incompatibilities between high level code and profiling.

runtime/mercury_term_size.[ch]:
	A new module to house a function to find and return term sizes
	stored in heap cells.

runtime/mercury_proc_id.h:
runtime/mercury_univ.h:
	New header files. mercury_proc_id.h contains the (unchanged)
	definition of MR_Proc_Id, while mercury_univ.h contains the
	definitions of the macros for manipulating univs that used to be
	in mercury_type_info.h, updated to use the new macros for allocating
	memory.

	In the absence of these header files, the following circularity
	would ensue:

	mercury_deep_profiling.h includes mercury_stack_layout.h
		- needs definition of MR_Proc_Id
	mercury_stack_layout.h needs mercury_type_info.h
		- needs definition of MR_PseudoTypeInfo
	mercury_type_info.h needs mercury_heap.h
		- needs heap allocation macros for MR_new_univ_on_hp
	mercury_heap.h includes mercury_deep_profiling.h
		- needs MR_current_call_site_dynamic for recording allocations

	Breaking the circular dependency in two places, not just one, is to
	minimize similar problems in the future.

runtime/mercury_stack_layout.h:
	Delete the definition of MR_Proc_Id, which is now in mercury_proc_id.h.

runtime/mercury_type_info.h:
	Delete the macros for manipulating univs, which are now in
	mercury_univ.h.

runtime/Mmakefile:
	Mention the new files.

runtime/mercury_imp.h:
runtime/mercury.h:
runtime/mercury_construct.c:
runtime/mercury_deep_profiling.h:
	Include the new files at appropriate points.

runtime/mercury.c:
	Change the names of the functions that create heap cells for
	hand-written code, since the interface to hand-written code has
	changed to include type information.

runtime/mercury_tabling.h:
	Delete some unused macros.

runtime/mercury_trace_base.c:
runtime/mercury_type_info.c:
	Use the new macros supplying type information when constructing lists.

scripts/canonical_grade_options.sh-subr:
	Fix an undefined sh variable bug that could cause error messages
	to come out without identifying the program they were from.

scripts/init_grade_options.sh-subr:
scripts/parse_grade_options.sh-subr:
scripts/canonical_grade_options.sh-subr:
scripts/mgnuc.in:
	Handle the new grade components and the options controlling them.

trace/mercury_trace_internal.c:
	Implement the mdb command "term_size <varspec>", which is like
	"print <varspec>", but prints the size of a term instead of its value.
	In non-term-size-profiling grades, it prints an error message.

	Replace the "proc_body" command with optional arguments to the "print"
	and "browse" commands.

doc/user_guide.tex:
	Add documentation of the term_size mdb command. Since the command is
	for implementors only, and works only in grades that are not yet ready
	for public consumption, the documentation is commented out.

	Add documentation of the new arguments of the print and browse mdb
	commands. Since they are for implementors only, the documentation
	is commented out.

trace/mercury_trace_vars.[ch]:
	Add the functions needed to implement the term_size command, and
	factor out the code common to the "size" and "print"/"browse" commands.

	Decide whether to print the name of a variable before invoking the
	supplied print or browse predicate on it based on a flag design for
	this purpose, instead of overloading the meaning of the output FILE *
	variable. This arrangement is much clearer.

trace/mercury_trace_browse.c:
trace/mercury_trace_external.c:
trace/mercury_trace_help.c:
	Supply type information when constructing terms.

browser/program_representation.m:
	Since the new library module term_size_prof_builtin never generates
	any events, mark it as such, so that the declarative debugger doesn't
	expect it to generate any.

	Do the same for the deep profiling builtin module.

tests/debugger/term_size_words.{m,inp,exp}:
tests/debugger/term_size_cells.{m,inp,exp}:
	Two new test cases, each testing one of the new grades.

tests/debugger/Mmakefile:
	Enable the two new test cases in their grades.

	Disable the tests sensitive to stack frame sizes in term size profiling
	grades.

tests/debugger/completion.exp:
	Add the new "term_size" mdb command to the list of command completions,
	and delete "proc_body".

tests/debugger/declarative/dependency.{inp,exp}:
	Use "print proc_body" instead of "proc_body".

tests/hard_coded/nondet_c.m:
tests/hard_coded/pragma_inline.m:
	Use MR_offset_incr_hp instead of MR_incr_hp to ensure that all places
	that allocate cells also allocate space for the term size slot if
	necessary.

tests/valid/Mmakefile:
	Disable the IL tests in term size profiling grades, since the term size
	profiling primitives haven't been (and probably won't be) implemented
	for the MLDS backends, and handle_options causes a compiler abort
	for grades that combine term size profiling and any one of IL, Java
	and high level C.
2003-10-20 07:29:59 +00:00

1449 lines
45 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1993-1995, 1997-2003 The University of Melbourne.
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING.LIB in the Mercury distribution.
%-----------------------------------------------------------------------------%
% File: array.m
% Main authors: fjh, bromage
% Stability: medium-low
% This module provides dynamically-sized one-dimensional arrays.
% Array indices start at zero.
% By default, the array__set and array__lookup procedures will check
% for bounds errors. But for better performance, it is possible to
% disable some of the checking by compiling with `--intermodule-optimization'
% and with the C macro symbol `ML_OMIT_ARRAY_BOUNDS_CHECKS'
% defined, e.g. by using `MCFLAGS=--intermodule-optimization' and
% `CFLAGS=-DML_OMIT_ARRAY_BOUNDS_CHECKS' in your Mmakefile,
% or by compiling with the command
% `mmc --intermodule-optimization --cflags -DML_OMIT_ARRAY_BOUNDS_CHECKS'.
%
% For maximum performance, all bounds checking can be disabled by
% recompiling this module using `CFLAGS=-DML_OMIT_ARRAY_BOUNDS_CHECKS'
% or `mmc --cflags -DML_OMIT_ARRAY_BOUNDS_CHECKS' as above. You can
% either recompile the entire library, or just copy `array.m' to your
% application's source directory and link with it directly instead of as
% part of the library.
%
% WARNING!
%
% Arrays are currently not unique objects - until this situation is
% resolved it is up to the programmer to ensure that arrays are used
% in such a way as to preserve correctness. In the absence of mode
% reordering, one should therefore assume that evaluation will take
% place in left-to-right order. For example, the following code will
% probably not work as expected (f is a function, A an array, I an
% index, and X an appropriate value):
%
% Y = f(A ^ elem(I) := X, A ^ elem(I))
%
% The compiler is likely to compile this as
%
% V0 = A ^ elem(I) := X,
% V1 = A ^ elem(I),
% Y = f(V0, V1)
%
% and will be unaware that the first line should be ordered
% *after* the second. The safest thing to do is write things out
% by hand in the form
%
% A0I = A0 ^ elem(I),
% A1 = A0 ^ elem(I) := X,
% Y = f(A1, A0I)
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- module array.
:- interface.
:- import_module list, std_util, random.
:- type array(T).
:- inst array(I) = bound(array(I)).
:- inst array == array(ground).
:- inst array_skel == array(free).
% XXX the current Mercury compiler doesn't support `ui' modes,
% so to work-around that problem, we currently don't use
% unique modes in this module.
% :- inst uniq_array(I) = unique(array(I)).
% :- inst uniq_array == uniq_array(unique).
:- inst uniq_array(I) = bound(array(I)). % XXX work-around
:- inst uniq_array == uniq_array(ground). % XXX work-around
:- inst uniq_array_skel == uniq_array(free).
:- mode array_di == di(uniq_array).
:- mode array_uo == out(uniq_array).
:- mode array_ui == in(uniq_array).
% :- inst mostly_uniq_array(I) = mostly_unique(array(I)).
% :- inst mostly_uniq_array == mostly_uniq_array(mostly_unique).
:- inst mostly_uniq_array(I) = bound(array(I)). % XXX work-around
:- inst mostly_uniq_array == mostly_uniq_array(ground). % XXX work-around
:- inst mostly_uniq_array_skel == mostly_uniq_array(free).
:- mode array_mdi == mdi(mostly_uniq_array).
:- mode array_muo == out(mostly_uniq_array).
:- mode array_mui == in(mostly_uniq_array).
% An `array__index_out_of_bounds' is the exception thrown
% on out-of-bounds array accesses. The string describes
% the predicate or function reporting the error.
:- type array__index_out_of_bounds
---> array__index_out_of_bounds(string).
%-----------------------------------------------------------------------------%
% array__make_empty_array(Array) creates an array of size zero
% starting at lower bound 0.
:- pred array__make_empty_array(array(T)).
:- mode array__make_empty_array(array_uo) is det.
:- func array__make_empty_array = array(T).
:- mode array__make_empty_array = array_uo is det.
% array__init(Size, Init, Array) creates an array
% with bounds from 0 to Size-1, with each element initialized to Init.
:- pred array__init(int, T, array(T)).
:- mode array__init(in, in, array_uo) is det.
:- func array__init(int, T) = array(T).
:- mode array__init(in, in) = array_uo is det.
% array/1 is a function that constructs an array from a list.
% (It does the same thing as the predicate array__from_list/2.)
% The syntax `array([...])' is used to represent arrays
% for io__read, io__write, term_to_type, and type_to_term.
:- func array(list(T)) = array(T).
:- mode array(in) = array_uo is det.
%-----------------------------------------------------------------------------%
% array__min returns the lower bound of the array.
% Note: in this implementation, the lower bound is always zero.
:- pred array__min(array(_T), int).
:- mode array__min(array_ui, out) is det.
:- mode array__min(in, out) is det.
:- func array__min(array(_T)) = int.
:- mode array__min(array_ui) = out is det.
:- mode array__min(in) = out is det.
% array__max returns the upper bound of the array.
:- pred array__max(array(_T), int).
:- mode array__max(array_ui, out) is det.
:- mode array__max(in, out) is det.
:- func array__max(array(_T)) = int.
:- mode array__max(array_ui) = out is det.
:- mode array__max(in) = out is det.
% array__size returns the length of the array,
% i.e. upper bound - lower bound + 1.
:- pred array__size(array(_T), int).
:- mode array__size(array_ui, out) is det.
:- mode array__size(in, out) is det.
:- func array__size(array(_T)) = int.
:- mode array__size(array_ui) = out is det.
:- mode array__size(in) = out is det.
% array__bounds returns the upper and lower bounds of an array.
% Note: in this implementation, the lower bound is always zero.
:- pred array__bounds(array(_T), int, int).
:- mode array__bounds(array_ui, out, out) is det.
:- mode array__bounds(in, out, out) is det.
% array__in_bounds checks whether an index is in the bounds
% of an array.
:- pred array__in_bounds(array(_T), int).
:- mode array__in_bounds(array_ui, in) is semidet.
:- mode array__in_bounds(in, in) is semidet.
%-----------------------------------------------------------------------------%
% array__lookup returns the Nth element of an array.
% Throws an exception if the index is out of bounds.
:- pred array__lookup(array(T), int, T).
:- mode array__lookup(array_ui, in, out) is det.
:- mode array__lookup(in, in, out) is det.
:- func array__lookup(array(T), int) = T.
:- mode array__lookup(array_ui, in) = out is det.
:- mode array__lookup(in, in) = out is det.
% array__semidet_lookup returns the Nth element of an array.
% It fails if the index is out of bounds.
:- pred array__semidet_lookup(array(T), int, T).
:- mode array__semidet_lookup(array_ui, in, out) is semidet.
:- mode array__semidet_lookup(in, in, out) is semidet.
% array__set sets the nth element of an array, and returns the
% resulting array (good opportunity for destructive update ;-).
% Throws an exception if the index is out of bounds.
:- pred array__set(array(T), int, T, array(T)).
:- mode array__set(array_di, in, in, array_uo) is det.
:- func array__set(array(T), int, T) = array(T).
:- mode array__set(array_di, in, in) = array_uo is det.
% array__semidet_set sets the nth element of an array,
% and returns the resulting array.
% It fails if the index is out of bounds.
:- pred array__semidet_set(array(T), int, T, array(T)).
:- mode array__semidet_set(array_di, in, in, array_uo) is semidet.
% array__slow_set sets the nth element of an array,
% and returns the resulting array. The initial array is not
% required to be unique, so the implementation may not be able to use
% destructive update.
% It is an error if the index is out of bounds.
:- pred array__slow_set(array(T), int, T, array(T)).
:- mode array__slow_set(array_ui, in, in, array_uo) is det.
:- mode array__slow_set(in, in, in, array_uo) is det.
:- func array__slow_set(array(T), int, T) = array(T).
:- mode array__slow_set(array_ui, in, in) = array_uo is det.
:- mode array__slow_set(in, in, in) = array_uo is det.
% array__semidet_slow_set sets the nth element of an array,
% and returns the resulting array. The initial array is not
% required to be unique, so the implementation may not be able to use
% destructive update.
% It fails if the index is out of bounds.
:- pred array__semidet_slow_set(array(T), int, T, array(T)).
:- mode array__semidet_slow_set(array_ui, in, in, array_uo) is semidet.
:- mode array__semidet_slow_set(in, in, in, array_uo) is semidet.
% Field selection for arrays.
% Array ^ elem(Index) = array__lookup(Array, Index).
:- func array__elem(int, array(T)) = T.
:- mode array__elem(in, array_ui) = out is det.
:- mode array__elem(in, in) = out is det.
% Field update for arrays.
% (Array ^ elem(Index) := Value) = array__set(Array, Index, Value).
:- func 'array__elem :='(int, array(T), T) = array(T).
:- mode 'array__elem :='(in, array_di, in) = array_uo is det.
%-----------------------------------------------------------------------------%
% array__copy(Array0, Array):
% Makes a new unique copy of an array.
:- pred array__copy(array(T), array(T)).
:- mode array__copy(array_ui, array_uo) is det.
:- mode array__copy(in, array_uo) is det.
:- func array__copy(array(T)) = array(T).
:- mode array__copy(array_ui) = array_uo is det.
:- mode array__copy(in) = array_uo is det.
% array__resize(Array0, Size, Init, Array):
% The array is expanded or shrunk to make it fit
% the new size `Size'. Any new entries are filled
% with `Init'.
:- pred array__resize(array(T), int, T, array(T)).
:- mode array__resize(array_di, in, in, array_uo) is det.
:- func array__resize(array(T), int, T) = array(T).
:- mode array__resize(array_di, in, in) = array_uo is det.
% array__shrink(Array0, Size, Array):
% The array is shrunk to make it fit the new size `Size'.
% Throws an exception if `Size' is larger than the size of `Array0'.
:- pred array__shrink(array(T), int, array(T)).
:- mode array__shrink(array_di, in, array_uo) is det.
:- func array__shrink(array(T), int) = array(T).
:- mode array__shrink(array_di, in) = array_uo is det.
% array__from_list takes a list,
% and returns an array containing those elements in
% the same order that they occurred in the list.
:- pred array__from_list(list(T), array(T)).
:- mode array__from_list(in, array_uo) is det.
:- func array__from_list(list(T)) = array(T).
:- mode array__from_list(in) = array_uo is det.
% array__to_list takes an array and returns a list containing
% the elements of the array in the same order that they
% occurred in the array.
:- pred array__to_list(array(T), list(T)).
:- mode array__to_list(array_ui, out) is det.
:- mode array__to_list(in, out) is det.
:- func array__to_list(array(T)) = list(T).
:- mode array__to_list(array_ui) = out is det.
:- mode array__to_list(in) = out is det.
% array__fetch_items takes an array and a lower and upper
% index, and places those items in the array between these
% indices into a list. It is an error if either index is
% out of bounds.
:- pred array__fetch_items(array(T), int, int, list(T)).
:- mode array__fetch_items(in, in, in, out) is det.
:- func array__fetch_items(array(T), int, int) = list(T).
:- mode array__fetch_items(array_ui, in, in) = out is det.
:- mode array__fetch_items(in, in, in) = out is det.
% array__bsearch takes an array, an element to be matched
% and a comparison predicate and returns the position of
% the first occurrence in the array of an element which is
% equivalent to the given one in the ordering provided.
% Assumes the array is sorted according to this ordering.
% Fails if the element is not present.
:- pred array__bsearch(array(T), T, comparison_pred(T), maybe(int)).
:- mode array__bsearch(array_ui, in, in(comparison_pred), out) is det.
:- mode array__bsearch(in, in, in(comparison_pred), out) is det.
:- func array__bsearch(array(T), T, comparison_func(T)) = maybe(int).
:- mode array__bsearch(array_ui, in, in(comparison_func)) = out is det.
:- mode array__bsearch(in, in, in(comparison_func)) = out is det.
% array__map(Closure, OldArray, NewArray) applys `Closure' to
% each of the elements of `OldArray' to create `NewArray'.
:- pred array__map(pred(T1, T2), array(T1), array(T2)).
:- mode array__map(pred(in, out) is det, array_di, array_uo) is det.
:- func array__map(func(T1) = T2, array(T1)) = array(T2).
:- mode array__map(func(in) = out is det, array_di) = array_uo is det.
:- func array_compare(array(T), array(T)) = comparison_result.
:- mode array_compare(in, in) = uo is det.
% array__sort(Array) returns a version of Array sorted
% into ascending order.
%
% This sort is not stable. That is, elements that
% compare/3 decides are equal will appear together in
% the sorted array, but not necessarily in the same
% order in which they occurred in the input array.
% This is primarily only an issue with types with
% user-defined equivalence for which `equivalent'
% objects are otherwise distinguishable.
%
:- func array__sort(array(T)) = array(T).
:- mode array__sort(array_di) = array_uo is det.
% array__foldl(Fn, Array, X) is equivalent to
% list__foldl(Fn, array__to_list(Array), X)
% but more efficient.
%
:- func array__foldl(func(T1, T2) = T2, array(T1), T2) = T2.
:- mode array__foldl(func(in, in) = out is det, array_ui, in) = out is det.
:- mode array__foldl(func(in, in) = out is det, in, in) = out is det.
:- mode array__foldl(func(in, di) = uo is det, array_ui, di) = uo is det.
:- mode array__foldl(func(in, di) = uo is det, in, di) = uo is det.
% array__foldr(Fn, Array, X) is equivalent to
% list__foldr(Fn, array__to_list(Array), X)
% but more efficient.
%
:- func array__foldr(func(T1, T2) = T2, array(T1), T2) = T2.
:- mode array__foldr(func(in, in) = out is det, array_ui, in) = out is det.
:- mode array__foldr(func(in, in) = out is det, in, in) = out is det.
:- mode array__foldr(func(in, di) = uo is det, array_ui, di) = uo is det.
:- mode array__foldr(func(in, di) = uo is det, in, di) = uo is det.
% array__random_permutation(A0, A, RS0, RS) permutes the elements in
% A0 given random seed RS0 and returns the permuted array in A
% and the next random seed in RS.
%
:- pred array__random_permutation(array(T), array(T),
random__supply, random__supply).
:- mode array__random_permutation(array_di, array_uo, mdi, muo) is det.
%-----------------------------------------------------------------------------%
:- implementation.
% Everything beyond here is not intended as part of the public interface,
% and will not appear in the Mercury Library Reference Manual.
:- import_module exception, int, require, string.
% MR_ArrayPtr is defined in runtime/mercury_library_types.h.
:- pragma foreign_type("C", array(T), "MR_ArrayPtr")
where equality is array__array_equal,
comparison is array__array_compare.
:- pragma foreign_type(il, array(T), "class [mscorlib]System.Array")
where equality is array__array_equal,
comparison is array__array_compare.
% unify/2 for arrays
:- pred array_equal(array(T)::in, array(T)::in) is semidet.
:- pragma export(array_equal(in, in), "ML_array_equal").
array_equal(Array1, Array2) :-
( if
array__size(Array1, Size),
array__size(Array2, Size)
then
array__equal_elements(0, Size, Array1, Array2)
else
fail
).
:- pred array__equal_elements(int, int, array(T), array(T)).
:- mode array__equal_elements(in, in, in, in) is semidet.
array__equal_elements(N, Size, Array1, Array2) :-
( N = Size ->
true
;
array__lookup(Array1, N, Elem),
array__lookup(Array2, N, Elem),
N1 = N + 1,
array__equal_elements(N1, Size, Array1, Array2)
).
% compare/3 for arrays
:- pred array_compare(comparison_result::uo, array(T)::in, array(T)::in)
is det.
:- pragma export(array_compare(uo, in, in), "ML_array_compare").
array_compare(Result, Array1, Array2) :-
array__size(Array1, Size1),
array__size(Array2, Size2),
compare(SizeResult, Size1, Size2),
( SizeResult = (=) ->
array__compare_elements(0, Size1, Array1, Array2, Result)
;
Result = SizeResult
).
:- pred array__compare_elements(int, int, array(T), array(T),
comparison_result).
:- mode array__compare_elements(in, in, in, in, uo) is det.
array__compare_elements(N, Size, Array1, Array2, Result) :-
( N = Size ->
Result = (=)
;
array__lookup(Array1, N, Elem1),
array__lookup(Array2, N, Elem2),
compare(ElemResult, Elem1, Elem2),
( ElemResult = (=) ->
N1 = N + 1,
array__compare_elements(N1, Size, Array1, Array2,
Result)
;
Result = ElemResult
)
).
%-----------------------------------------------------------------------------%
:- pred bounds_checks is semidet.
:- pragma inline(bounds_checks/0).
:- pragma foreign_proc("C",
bounds_checks,
[will_not_call_mercury, promise_pure, thread_safe],
"
#ifdef ML_OMIT_ARRAY_BOUNDS_CHECKS
SUCCESS_INDICATOR = MR_FALSE;
#else
SUCCESS_INDICATOR = MR_TRUE;
#endif
").
:- pragma foreign_proc("MC++",
bounds_checks,
[will_not_call_mercury, promise_pure, thread_safe],
"
#if ML_OMIT_ARRAY_BOUNDS_CHECKS
SUCCESS_INDICATOR = MR_FALSE;
#else
SUCCESS_INDICATOR = MR_TRUE;
#endif
").
%-----------------------------------------------------------------------------%
:- pragma foreign_decl("C", "
#include ""mercury_heap.h"" /* for MR_maybe_record_allocation() */
#include ""mercury_library_types.h"" /* for MR_ArrayPtr */
/*
** We do not yet record term sizes for arrays in term size profiling
** grades. Doing so would require
**
** - modifying ML_alloc_array to allocate an extra word for the size;
** - modifying all the predicates that call ML_alloc_array to compute the
** size of the array (the sum of the sizes of the elements and the size of
** the array itself);
** - modifying all the predicates that update array elements to compute the
** difference between the sizes of the terms being added to and deleted from
** the array, and updating the array size accordingly.
*/
#define ML_alloc_array(newarray, arraysize, proclabel) \
MR_offset_incr_hp_msg(MR_LVALUE_CAST(MR_Word, (newarray)), \
0, (arraysize), proclabel, ""array:array/1"")
").
:- pragma foreign_decl("C", "
void ML_init_array(MR_ArrayPtr, MR_Integer size, MR_Word item);
").
:- pragma foreign_code("C", "
/*
** The caller is responsible for allocating the memory for the array.
** This routine does the job of initializing the already-allocated memory.
*/
void
ML_init_array(MR_ArrayPtr array, MR_Integer size, MR_Word item)
{
MR_Integer i;
array->size = size;
for (i = 0; i < size; i++) {
array->elements[i] = item;
}
}
").
array__init(Size, Item, Array) :-
( Size < 0 ->
error("array__init: negative size")
;
array__init_2(Size, Item, Array)
).
:- pred array__init_2(int, T, array(T)).
:- mode array__init_2(in, in, array_uo) is det.
:- pragma foreign_proc("C",
array__init_2(Size::in, Item::in, Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
ML_alloc_array(Array, Size + 1, MR_PROC_LABEL);
ML_init_array(Array, Size, Item);
").
:- pragma foreign_proc("C",
array__make_empty_array(Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
ML_alloc_array(Array, 1, MR_PROC_LABEL);
ML_init_array(Array, 0, 0);
").
:- pragma foreign_proc("C#",
array__init_2(Size::in, Item::in, Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
Array = System.Array.CreateInstance(Item.GetType(), Size);
for (int i = 0; i < Size; i++) {
Array.SetValue(Item, i);
}
").
:- pragma foreign_proc("C#",
array__make_empty_array(Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
// XXX A better solution then using the null pointer to represent
// the empty array would be to create an array of size 0. However
// we need to determine the element type of the array before we can
// do that. This could be done by examing the RTTI of the array
// type and then using System.Type.GetType(""<mercury type>"") to
// determine it. However constructing the <mercury type> string is
// a non-trival amount of work.
Array = null;
").
%-----------------------------------------------------------------------------%
:- pragma foreign_proc("C",
array__min(Array::array_ui, Min::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
/* Array not used */
Min = 0;
").
:- pragma foreign_proc("C",
array__min(Array::in, Min::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
/* Array not used */
Min = 0;
").
:- pragma foreign_proc("C#",
array__min(Array::array_ui, Min::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
/* Array not used */
Min = 0;
").
:- pragma foreign_proc("C#",
array__min(Array::in, Min::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
/* Array not used */
Min = 0;
").
:- pragma promise_pure(array__max/2).
:- pragma foreign_proc("C",
array__max(Array::array_ui, Max::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
Max = Array->size - 1;
").
:- pragma foreign_proc("C",
array__max(Array::in, Max::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
Max = Array->size - 1;
").
:- pragma foreign_proc("C#",
array__max(Array::array_ui, Max::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
if (Array != null) {
Max = Array.Length - 1;
} else {
Max = -1;
}
").
:- pragma foreign_proc("C#",
array__max(Array::in, Max::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
if (Array != null) {
Max = Array.Length - 1;
} else {
Max = -1;
}
").
array__bounds(Array, Min, Max) :-
array__min(Array, Min),
array__max(Array, Max).
%-----------------------------------------------------------------------------%
:- pragma foreign_proc("C",
array__size(Array::array_ui, Max::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
Max = Array->size;
").
:- pragma foreign_proc("C",
array__size(Array::in, Max::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
Max = Array->size;
").
:- pragma foreign_proc("C#",
array__size(Array::array_ui, Max::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
if (Array != null) {
Max = Array.Length;
} else {
Max = 0;
}
").
:- pragma foreign_proc("C#",
array__size(Array::in, Max::out),
[will_not_call_mercury, promise_pure, thread_safe],
"
if (Array != null) {
Max = Array.Length;
} else {
Max = 0;
}
").
%-----------------------------------------------------------------------------%
array__in_bounds(Array, Index) :-
array__bounds(Array, Min, Max),
Min =< Index, Index =< Max.
array__semidet_lookup(Array, Index, Item) :-
( if array__in_bounds(Array, Index) then
array__unsafe_lookup(Array, Index, Item)
else
fail
).
array__semidet_set(Array0, Index, Item, Array) :-
( if array__in_bounds(Array0, Index) then
array__unsafe_set(Array0, Index, Item, Array)
else
fail
).
array__semidet_slow_set(Array0, Index, Item, Array) :-
( if array__in_bounds(Array0, Index) then
array__slow_set(Array0, Index, Item, Array)
else
fail
).
array__slow_set(Array0, Index, Item, Array) :-
array__copy(Array0, Array1),
array__set(Array1, Index, Item, Array).
%-----------------------------------------------------------------------------%
array__lookup(Array, Index, Item) :-
( bounds_checks, \+ array__in_bounds(Array, Index) ->
out_of_bounds_error(Array, Index, "array__lookup")
;
array__unsafe_lookup(Array, Index, Item)
).
:- pred array__unsafe_lookup(array(T), int, T).
:- mode array__unsafe_lookup(array_ui, in, out) is det.
:- mode array__unsafe_lookup(in, in, out) is det.
:- pragma foreign_proc("C",
array__unsafe_lookup(Array::array_ui, Index::in, Item::out),
[will_not_call_mercury, promise_pure, thread_safe],
"{
Item = Array->elements[Index];
}").
:- pragma foreign_proc("C",
array__unsafe_lookup(Array::in, Index::in, Item::out),
[will_not_call_mercury, promise_pure, thread_safe],
"{
Item = Array->elements[Index];
}").
:- pragma foreign_proc("C#",
array__unsafe_lookup(Array::array_ui, Index::in, Item::out),
[will_not_call_mercury, promise_pure, thread_safe],
"{
Item = Array.GetValue(Index);
}").
:- pragma foreign_proc("C#",
array__unsafe_lookup(Array::in, Index::in, Item::out),
[will_not_call_mercury, promise_pure, thread_safe],
"{
Item = Array.GetValue(Index);
}").
%-----------------------------------------------------------------------------%
array__set(Array0, Index, Item, Array) :-
( bounds_checks, \+ array__in_bounds(Array0, Index) ->
out_of_bounds_error(Array0, Index, "array__set")
;
array__unsafe_set(Array0, Index, Item, Array)
).
:- pred array__unsafe_set(array(T), int, T, array(T)).
:- mode array__unsafe_set(array_di, in, in, array_uo) is det.
:- pragma foreign_proc("C",
array__unsafe_set(Array0::array_di, Index::in,
Item::in, Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"{
Array0->elements[Index] = Item; /* destructive update! */
Array = Array0;
}").
:- pragma foreign_proc("C#",
array__unsafe_set(Array0::array_di, Index::in,
Item::in, Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"{
Array0.SetValue(Item, Index); /* destructive update! */
Array = Array0;
}").
%-----------------------------------------------------------------------------%
/****
lower bounds other than zero are not supported
% array__resize takes an array and new lower and upper bounds.
% the array is expanded or shrunk at each end to make it fit
% the new bounds.
:- pred array__resize(array(T), int, int, array(T)).
:- mode array__resize(in, in, in, out) is det.
****/
:- pragma foreign_decl("C", "
void ML_resize_array(MR_ArrayPtr new_array, MR_ArrayPtr old_array,
MR_Integer array_size, MR_Word item);
").
:- pragma foreign_code("C", "
/*
** The caller is responsible for allocating the storage for the new array.
** This routine does the job of copying the old array elements to the
** new array, initializing any additional elements in the new array,
** and deallocating the old array.
*/
void
ML_resize_array(MR_ArrayPtr array, MR_ArrayPtr old_array,
MR_Integer array_size, MR_Word item)
{
MR_Integer i;
MR_Integer elements_to_copy;
elements_to_copy = old_array->size;
if (elements_to_copy > array_size) {
elements_to_copy = array_size;
}
array->size = array_size;
for (i = 0; i < elements_to_copy; i++) {
array->elements[i] = old_array->elements[i];
}
for (; i < array_size; i++) {
array->elements[i] = item;
}
/*
** since the mode on the old array is `array_di', it is safe to
** deallocate the storage for it
*/
#ifdef MR_CONSERVATIVE_GC
GC_FREE(old_array);
#endif
}
").
:- pragma foreign_proc("C",
array__resize(Array0::array_di, Size::in, Item::in,
Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
if ((Array0)->size == Size) {
Array = Array0;
} else {
ML_alloc_array(Array, Size + 1, MR_PROC_LABEL);
ML_resize_array(Array, Array0, Size, Item);
}
").
:- pragma foreign_proc("C#",
array__resize(Array0::array_di, Size::in, Item::in,
Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
if (Array0 == null) {
Array = System.Array.CreateInstance(Item.GetType(), Size);
for (int i = 0; i < Size; i++) {
Array.SetValue(Item, i);
}
}
else if (Array0.Length == Size) {
Array = Array0;
} else if (Array0.Length > Size) {
Array = System.Array.CreateInstance(Item.GetType(), Size);
System.Array.Copy(Array0, Array, Size);
} else {
Array = System.Array.CreateInstance(Item.GetType(), Size);
System.Array.Copy(Array0, Array, Array0.Length);
for (int i = Array0.Length; i < Size; i++) {
Array.SetValue(Item, i);
}
}
").
%-----------------------------------------------------------------------------%
:- pragma foreign_decl("C", "
void ML_shrink_array(MR_ArrayPtr array, MR_ArrayPtr old_array,
MR_Integer array_size);
").
:- pragma foreign_code("C", "
/*
** The caller is responsible for allocating the storage for the new array.
** This routine does the job of copying the old array elements to the
** new array and deallocating the old array.
*/
void
ML_shrink_array(MR_ArrayPtr array, MR_ArrayPtr old_array,
MR_Integer array_size)
{
MR_Integer i;
array->size = array_size;
for (i = 0; i < array_size; i++) {
array->elements[i] = old_array->elements[i];
}
/*
** since the mode on the old array is `array_di', it is safe to
** deallocate the storage for it
*/
#ifdef MR_CONSERVATIVE_GC
GC_FREE(old_array);
#endif
}
").
array__shrink(Array0, Size, Array) :-
OldSize = array__size(Array0),
( Size > OldSize ->
error("array__shrink: can't shrink to a larger size")
; Size = OldSize ->
Array = Array0
;
array__shrink_2(Array0, Size, Array)
).
:- pred array__shrink_2(array(T), int, array(T)).
:- mode array__shrink_2(array_di, in, array_uo) is det.
:- pragma foreign_proc("C",
array__shrink_2(Array0::array_di, Size::in, Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
ML_alloc_array(Array, Size + 1, MR_PROC_LABEL);
ML_shrink_array(Array, Array0, Size);
").
:- pragma foreign_proc("C#",
array__shrink_2(Array0::array_di, Size::in, Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
Array = System.Array.CreateInstance(
Array0.GetType().GetElementType(), Size);
System.Array.Copy(Array0, Array, Size);
").
%-----------------------------------------------------------------------------%
:- pragma foreign_decl("C", "
void ML_copy_array(MR_ArrayPtr array, MR_ConstArrayPtr old_array);
").
:- pragma foreign_code("C", "
/*
** The caller is responsible for allocating the storage for the new array.
** This routine does the job of copying the array elements.
*/
void
ML_copy_array(MR_ArrayPtr array, MR_ConstArrayPtr old_array)
{
/*
** Any changes to this function will probably also require
** changes to deepcopy() in runtime/deep_copy.c.
*/
MR_Integer i;
MR_Integer array_size;
array_size = old_array->size;
array->size = array_size;
for (i = 0; i < array_size; i++) {
array->elements[i] = old_array->elements[i];
}
}
").
:- pragma foreign_proc("C",
array__copy(Array0::array_ui, Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
ML_alloc_array(Array, Array0->size + 1, MR_PROC_LABEL);
ML_copy_array(Array, (MR_ConstArrayPtr) Array0);
").
:- pragma foreign_proc("C",
array__copy(Array0::in, Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
ML_alloc_array(Array, Array0->size + 1, MR_PROC_LABEL);
ML_copy_array(Array, (MR_ConstArrayPtr) Array0);
").
:- pragma foreign_proc("C#",
array__copy(Array0::array_ui, Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
// XXX we implement the same as ML_copy_array, which doesn't appear
// to deep copy the array elements
Array = System.Array.CreateInstance(
Array0.GetType().GetElementType(), Array0.Length);
System.Array.Copy(Array0, Array, Array0.Length);
").
:- pragma foreign_proc("C#",
array__copy(Array0::in, Array::array_uo),
[will_not_call_mercury, promise_pure, thread_safe],
"
// XXX we implement the same as ML_copy_array, which doesn't appear
// to deep copy the array elements
Array = System.Array.CreateInstance(
Array0.GetType().GetElementType(), Array0.Length);
System.Array.Copy(Array0, Array, Array0.Length);
").
%-----------------------------------------------------------------------------%
array(List) = Array :-
array__from_list(List, Array).
array__from_list([], Array) :-
array__make_empty_array(Array).
array__from_list(List, Array) :-
List = [ Head | Tail ],
list__length(List, Len),
array__init(Len, Head, Array0),
array__insert_items(Tail, 1, Array0, Array).
%-----------------------------------------------------------------------------%
:- pred array__insert_items(list(T), int, array(T), array(T)).
:- mode array__insert_items(in, in, array_di, array_uo) is det.
array__insert_items([], _N, Array, Array).
array__insert_items([Head|Tail], N, Array0, Array) :-
array__set(Array0, N, Head, Array1),
N1 = N + 1,
array__insert_items(Tail, N1, Array1, Array).
%-----------------------------------------------------------------------------%
array__to_list(Array, List) :-
array__bounds(Array, Low, High),
array__fetch_items(Array, Low, High, List).
%-----------------------------------------------------------------------------%
array__fetch_items(Array, Low, High, List) :-
List = foldr_0(func(X, Xs) = [X | Xs], Array, [], Low, High).
%-----------------------------------------------------------------------------%
array__bsearch(A, El, Compare, Result) :-
array__bounds(A, Lo, Hi),
array__bsearch_2(A, Lo, Hi, El, Compare, Result).
:- pred array__bsearch_2(array(T), int, int, T,
pred(T, T, comparison_result), maybe(int)).
:- mode array__bsearch_2(in, in, in, in, pred(in, in, out) is det,
out) is det.
array__bsearch_2(Array, Lo, Hi, El, Compare, Result) :-
Width = Hi - Lo,
% If Width < 0, there is no range left.
( Width < 0 ->
Result = no
;
% If Width == 0, we may just have found our element.
% Do a Compare to check.
( Width = 0 ->
array__lookup(Array, Lo, X),
( call(Compare, El, X, (=)) ->
Result = yes(Lo)
;
Result = no
)
;
% Otherwise find the middle element of the range
% and check against that.
Mid = (Lo + Hi) >> 1, % `>> 1' is hand-optimized `div 2'.
array__lookup(Array, Mid, XMid),
call(Compare, XMid, El, Comp),
( Comp = (<),
Mid1 = Mid + 1,
array__bsearch_2(Array, Mid1, Hi, El, Compare, Result)
; Comp = (=),
array__bsearch_2(Array, Lo, Mid, El, Compare, Result)
; Comp = (>),
Mid1 = Mid - 1,
array__bsearch_2(Array, Lo, Mid1, El, Compare, Result)
)
)
).
%-----------------------------------------------------------------------------%
array__map(Closure, OldArray, NewArray) :-
( array__semidet_lookup(OldArray, 0, Elem0) ->
array__size(OldArray, Size),
call(Closure, Elem0, Elem),
array__init(Size, Elem, NewArray0),
array__map_2(1, Size, Closure, OldArray,
NewArray0, NewArray)
;
array__make_empty_array(NewArray)
).
:- pred array__map_2(int, int, pred(T1, T2), array(T1), array(T2), array(T2)).
:- mode array__map_2(in, in, pred(in, out) is det, in, array_di, array_uo)
is det.
array__map_2(N, Size, Closure, OldArray, NewArray0, NewArray) :-
( N >= Size ->
NewArray = NewArray0
;
array__lookup(OldArray, N, OldElem),
Closure(OldElem, NewElem),
array__set(NewArray0, N, NewElem, NewArray1),
array__map_2(N + 1, Size, Closure, OldArray,
NewArray1, NewArray)
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% Ralph Becket <rwab1@cam.sri.com> 24/04/99
% Function forms added.
array__make_empty_array = A :-
array__make_empty_array(A).
array__init(N, X) = A :-
array__init(N, X, A).
array__min(A) = N :-
array__min(A, N).
array__max(A) = N :-
array__max(A, N).
array__size(A) = N :-
array__size(A, N).
array__lookup(A, N) = X :-
array__lookup(A, N, X).
array__set(A1, N, X) = A2 :-
array__set(A1, N, X, A2).
array__slow_set(A1, N, X) = A2 :-
array__slow_set(A1, N, X, A2).
array__copy(A1) = A2 :-
array__copy(A1, A2).
array__resize(A1, N, X) = A2 :-
array__resize(A1, N, X, A2).
array__shrink(A1, N) = A2 :-
array__shrink(A1, N, A2).
array__from_list(Xs) = A :-
array__from_list(Xs, A).
array__to_list(A) = Xs :-
array__to_list(A, Xs).
array__fetch_items(A, N1, N2) = Xs :-
array__fetch_items(A, N1, N2, Xs).
array__bsearch(A, X, F) = MN :-
P = ( pred(X1::in, X2::in, C::out) is det :- C = F(X1, X2) ),
array__bsearch(A, X, P, MN).
array__map(F, A1) = A2 :-
P = ( pred(X::in, Y::out) is det :- Y = F(X) ),
array__map(P, A1, A2).
array_compare(A1, A2) = C :-
array_compare(C, A1, A2).
array__elem(Index, Array) = array__lookup(Array, Index).
'array__elem :='(Index, Array, Value) = array__set(Array, Index, Value).
% ---------------------------------------------------------------------------- %
% array__sort/1 has type specialised versions for arrays of
% ints and strings on the expectation that these constitute
% the common case and are hence worth providing a fast-path.
%
% Experiments indicate that type specialisation improves
% array__sort/1 by a factor of 30-40%.
%
:- pragma type_spec(array__sort/1, T = int).
:- pragma type_spec(array__sort/1, T = string).
array__sort(A) = samsort_subarray(A, array__min(A), array__max(A)).
%------------------------------------------------------------------------------%
array__random_permutation(A0, A, RS0, RS) :-
Lo = array__min(A0),
Hi = array__max(A0),
Sz = array__size(A0),
permutation_2(Lo, Lo, Hi, Sz, A0, A, RS0, RS).
:- pred permutation_2(int, int, int, int, array(T), array(T),
random__supply, random__supply).
:- mode permutation_2(in, in, in, in, array_di, array_uo, mdi, muo) is det.
permutation_2(I, Lo, Hi, Sz, A0, A, RS0, RS) :-
( if I > Hi then
A = A0,
RS = RS0
else
random__random(R, RS0, RS1),
J = Lo + (R `rem` Sz),
A1 = swap_elems(A0, I, J),
permutation_2(I + 1, Lo, Hi, Sz, A1, A, RS1, RS)
).
%------------------------------------------------------------------------------%
:- func swap_elems(array(T), int, int) = array(T).
:- mode swap_elems(array_di, in, in) = array_uo is det.
swap_elems(A0, I, J) = A :-
XI = A0 ^ elem(I),
XJ = A0 ^ elem(J),
A = ((A0 ^ elem(I) := XJ)
^ elem(J) := XI).
% ---------------------------------------------------------------------------- %
array__foldl(Fn, A, X) =
foldl_0(Fn, A, X, array__min(A), array__max(A)).
:- func foldl_0(func(T1, T2) = T2, array(T1), T2, int, int) = T2.
:- mode foldl_0(func(in, in) = out is det, array_ui, in, in, in) = out is det.
:- mode foldl_0(func(in, in) = out is det, in, in, in, in) = out is det.
:- mode foldl_0(func(in, di) = uo is det, array_ui, di, in, in) = uo is det.
:- mode foldl_0(func(in, di) = uo is det, in, di, in, in) = uo is det.
foldl_0(Fn, A, X, I, Max) =
( if Max < I then X
else foldl_0(Fn, A, Fn(A ^ elem(I), X), I + 1, Max)
).
% ---------------------------------------------------------------------------- %
array__foldr(Fn, A, X) =
foldr_0(Fn, A, X, array__min(A), array__max(A)).
:- func foldr_0(func(T1, T2) = T2, array(T1), T2, int, int) = T2.
:- mode foldr_0(func(in, in) = out is det, array_ui, in, in, in) = out is det.
:- mode foldr_0(func(in, in) = out is det, in, in, in, in) = out is det.
:- mode foldr_0(func(in, di) = uo is det, array_ui, di, in, in) = uo is det.
:- mode foldr_0(func(in, di) = uo is det, in, di, in, in) = uo is det.
foldr_0(Fn, A, X, Min, I) =
( if I < Min then X
else foldr_0(Fn, A, Fn(A ^ elem(I), X), Min, I - 1)
).
% ---------------------------------------------------------------------------- %
% ---------------------------------------------------------------------------- %
% SAMsort (smooth applicative merge) invented by R.A. O'Keefe.
%
% SAMsort is a mergesort variant that works by identifying contiguous
% monotonic sequences and merging them, thereby taking advantage of
% any existing order in the input sequence.
%
:- func samsort_subarray(array(T), int, int) = array(T).
:- mode samsort_subarray(array_di, in, in) = array_uo is det.
:- pragma type_spec(samsort_subarray/3, T = int).
:- pragma type_spec(samsort_subarray/3, T = string).
samsort_subarray(A0, Lo, Hi) = A :-
samsort_up(0, A0, _, array__copy(A0), A, Lo, Hi, Lo).
:- pred samsort_up(int, array(T), array(T), array(T), array(T), int, int, int).
:- mode samsort_up(in, array_di, array_uo, array_di, array_uo, in, in, in)
is det.
:- pragma type_spec(samsort_up/8, T = int).
:- pragma type_spec(samsort_up/8, T = string).
% Precondition:
% We are N levels from the bottom (leaf nodes) of the tree.
% A0 is sorted from Lo .. I - 1.
% A0 and B0 are identical from I .. Hi.
% Postcondition:
% B is sorted from Lo .. Hi.
%
samsort_up(N, A0, A, B0, B, Lo, Hi, I) :-
( if I > Hi then
A = A0,
B = B0
else if N > 0 then
samsort_down(N - 1, B0, B1, A0, A1, I, Hi, J),
% A1 is sorted from I .. J - 1.
% A1 and B1 are identical from J .. Hi.
B2 = merge_subarrays(A1, B1, Lo, I - 1, I, J - 1, Lo),
A2 = A1,
% B2 is sorted from Lo .. J - 1.
samsort_up(N + 1, B2, B, A2, A, Lo, Hi, J)
else /* N = 0, I = Lo */
copy_run_ascending(A0, B0, B1, Lo, Hi, J),
% B1 is sorted from Lo .. J - 1.
samsort_up(N + 1, B1, B, A0, A, Lo, Hi, J)
).
:- pred samsort_down(int,array(T),array(T),array(T),array(T),int,int,int).
:- mode samsort_down(in, array_di, array_uo, array_di, array_uo, in, in, out)
is det.
:- pragma type_spec(samsort_down/8, T = int).
:- pragma type_spec(samsort_down/8, T = string).
% Precondition:
% We are N levels from the bottom (leaf nodes) of the tree.
% A0 and B0 are identical from Lo .. Hi.
% Postcondition:
% B is sorted from Lo .. I - 1.
% A and B are identical from I .. Hi.
%
samsort_down(N, A0, A, B0, B, Lo, Hi, I) :-
( if Lo > Hi then
A = A0,
B = B0,
I = Lo
else if N > 0 then
samsort_down(N - 1, B0, B1, A0, A1, Lo, Hi, J),
samsort_down(N - 1, B1, B2, A1, A2, J, Hi, I),
% A2 is sorted from Lo .. J - 1.
% A2 is sorted from J .. I - 1.
A = A2,
B = merge_subarrays(A2, B2, Lo, J - 1, J, I - 1, Lo)
% B is sorted from Lo .. I - 1.
else
A = A0,
copy_run_ascending(A0, B0, B, Lo, Hi, I)
% B is sorted from Lo .. I - 1.
).
%------------------------------------------------------------------------------%
:- pred copy_run_ascending(array(T), array(T), array(T), int, int, int).
:- mode copy_run_ascending(array_ui, array_di, array_uo, in, in, out) is det.
:- pragma type_spec(copy_run_ascending/6, T = int).
:- pragma type_spec(copy_run_ascending/6, T = string).
copy_run_ascending(A, B0, B, Lo, Hi, I) :-
( if Lo < Hi, compare((>), A ^ elem(Lo), A ^ elem(Lo + 1)) then
I = search_until((<), A, Lo, Hi),
B = copy_subarray_reverse(A, B0, Lo, I - 1, I - 1)
else
I = search_until((>), A, Lo, Hi),
B = copy_subarray(A, B0, Lo, I - 1, Lo)
).
%------------------------------------------------------------------------------%
:- func search_until(comparison_result, array(T), int, int) = int.
:- mode search_until(in, array_ui, in, in) = out is det.
:- pragma type_spec(search_until/4, T = int).
:- pragma type_spec(search_until/4, T = string).
search_until(R, A, Lo, Hi) =
( if Lo < Hi, not compare(R, A ^ elem(Lo), A ^ elem(Lo + 1)) then
search_until(R, A, Lo + 1, Hi)
else
Lo + 1
).
%------------------------------------------------------------------------------%
:- func copy_subarray(array(T), array(T), int, int, int) = array(T).
:- mode copy_subarray(array_ui, array_di, in, in, in) = array_uo is det.
:- pragma type_spec(copy_subarray/5, T = int).
:- pragma type_spec(copy_subarray/5, T = string).
copy_subarray(A, B, Lo, Hi, I) =
( if Lo =< Hi then
copy_subarray(A, B ^ elem(I) := A ^ elem(Lo),
Lo + 1, Hi, I + 1)
else
B
).
%------------------------------------------------------------------------------%
:- func copy_subarray_reverse(array(T), array(T), int, int, int) = array(T).
:- mode copy_subarray_reverse(array_ui, array_di, in, in, in) = array_uo is det.
:- pragma type_spec(copy_subarray_reverse/5, T = int).
:- pragma type_spec(copy_subarray_reverse/5, T = string).
copy_subarray_reverse(A, B, Lo, Hi, I) =
( if Lo =< Hi then
copy_subarray_reverse(A, B ^ elem(I) := A ^ elem(Lo),
Lo + 1, Hi, I - 1)
else
B
).
%------------------------------------------------------------------------------%
% merges the two sorted consecutive subarrays Lo1 .. Hi1 and
% Lo2 .. Hi2 from A into the subarray starting at I in B.
%
:- func merge_subarrays(array(T), array(T), int, int, int, int, int) = array(T).
:- mode merge_subarrays(array_ui, array_di, in, in, in, in, in) = array_uo
is det.
:- pragma type_spec(merge_subarrays/7, T = int).
:- pragma type_spec(merge_subarrays/7, T = string).
merge_subarrays(A, B0, Lo1, Hi1, Lo2, Hi2, I) = B :-
( if Lo1 > Hi1 then
B = copy_subarray(A, B0, Lo2, Hi2, I)
else if Lo2 > Hi2 then
B = copy_subarray(A, B0, Lo1, Hi1, I)
else
X1 = A ^ elem(Lo1),
X2 = A ^ elem(Lo2),
compare(R, X1, X2),
(
R = (<),
B = merge_subarrays(A, B0^elem(I) := X1,
Lo1+1, Hi1, Lo2, Hi2, I+1)
;
R = (=),
B = merge_subarrays(A, B0^elem(I) := X1,
Lo1+1, Hi1, Lo2, Hi2, I+1)
;
R = (>),
B = merge_subarrays(A, B0^elem(I) := X2,
Lo1, Hi1, Lo2+1, Hi2, I+1)
)
).
%------------------------------------------------------------------------------%
% throw an exception indicating an array bounds error
:- pred out_of_bounds_error(array(T), int, string).
:- mode out_of_bounds_error(array_ui, in, in) is erroneous.
:- mode out_of_bounds_error(in, in, in) is erroneous.
% Note: we deliberately do not include the array element type name
% in the error message here, for performance reasons:
% using the type name could prevent the compiler from optimizing
% away the construction of the type_info in the caller,
% because it would prevent unused argument elimination.
% Performance is important here, because array__set and array__lookup
% are likely to be used in the inner loops of performance-critical
% applications.
out_of_bounds_error(Array, Index, PredName) :-
array__bounds(Array, Min, Max),
throw(array__index_out_of_bounds(
string__format("%s: index %d not in range [%d, %d]",
[s(PredName), i(Index), i(Min), i(Max)]))).
%------------------------------------------------------------------------------%
%------------------------------------------------------------------------------%