Files
mercury/library/array2d.m
Zoltan Somogyi a19bcaaed5 Updates to programming style in the library.
library/bt_array.m:
    Reorder the arguments of the resize and shrink predicates
    to make them easier to use with state variables.

tests/general/array_test.m:
    Update the test calls to these two predicates.

library/version_array2d.m:
    Rewrite part of this module using a programming style that includes
    variable names that are longer than one character :-(. In the absence
    of comprehensive, or even basic, test cases, leave the rest using
    the old style.

    Add a non-field-syntax lookup operation.

    Mark the site of a probable bug.

library/version_bitmap.m:
    Add non-field-syntax versions of the get_bit and set_bit operations.

NEWS.md:
    Announce the reordering and the new predicates above.

library/getopt.m:
library/getopt_io.m:
    Apply to getopt_io.m an update that was previously applied to getopt.m,
    even though getopt.m is now computed from getopt_io.m.

library/array2d.m:
library/bit_buffer.m:
library/bit_buffer.read.m:
library/bit_buffer.write.m:
library/bitmap.m:
library/cord.m:
library/edit_distance.m:
library/edit_seq.m:
library/fat_sparse_bitset.m:
library/fatter_sparse_bitset.m:
library/list.m:
library/one_or_more.m:
library/pair.m:
library/ra_list.m:
library/rbtree.m:
library/set_bbbtree.m:
library/set_ctree234.m:
library/set_ordlist.m:
library/set_tree234.m:
library/set_unordlist.m:
library/solutions.m:
library/sparse_bitset.m:
library/test_bitset.m:
library/thread.barrier.m:
library/thread.m:
library/thread.semaphore.m:
library/time.m:
library/tree_bitset.m:
library/version_array.m:
library/version_hash_table.m:
library/version_store.m:
    General updates to style, the main ones being the following.

    Using standard names for type variables:

    - K and V for key and value types in map-like structures
    - A to F for types of accumulators
    - T for most everything else, possibly with a numeric suffix
      to distinguish two separate types that nevertheless play
      similar roles.

    (I left descriptive type var names such as Stream and Store unchanged.)

    Adding or expanding descriptions of exported predicates and functions.

    Declaring and defining field syntax getters and setters without using ^,
    while keeping the use of field syntax, including ^, in the operations'
    descriptions.

    Defining field syntax operations operations in terms of the
    non-field-syntax versions, not vice versa.

    Defining function versions of operations in terms of the predicate
    versions, not vice versa.
2024-12-29 20:09:09 +11:00

374 lines
12 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2003, 2005-2007, 2011-2012 The University of Melbourne.
% Copyright (C) 2013-2022, 2024 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%---------------------------------------------------------------------------%
%
% File: array2d.m.
% Author: Ralph Becket <rafe@cs.mu.oz.au>.
% Stability: medium-low.
%
% Two-dimensional rectangular (i.e. not ragged) array ADT.
%
% XXX The same caveats re: uniqueness of arrays apply to array2ds.
%
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- module array2d.
:- interface.
:- import_module array.
:- import_module list.
%---------------------------------------------------------------------------%
% An array2d is a two-dimensional array stored in row-major order
% (that is, the elements of the first row in left-to-right
% order, followed by the elements of the second row and so forth.)
%
:- type array2d(T).
:- inst array2d for array2d/1
---> array2d(ground, ground, array).
% XXX These are work-arounds until we get nested uniqueness working.
%
:- mode array2d_di == di(array2d).
:- mode array2d_ui == in(array2d).
:- mode array2d_uo == out(array2d).
% init(NumRows, NumColumns, Elem):
% Creates a 2d array with the given numbers of rows and columns
% whose every element is set to Elem.
%
% Throws an exception if either NumRows or NumColumns is negative.
%
:- func init(int, int, T) = array2d(T).
:- mode init(in, in, in) = array2d_uo is det.
% array2d([[X11, ..., X1N], ..., [XM1, ..., XMN]]) constructs an array2d
% of size M * N, with the special case that bounds(array2d([]), 0, 0).
%
% In other words, the elements of the top level list each represent
% one row, and each row is itself a list of the values in the columns
% of that row.
%
% Throws an exception unless all rows have the same number of columns.
%
:- func array2d(list(list(T))) = array2d(T).
:- mode array2d(in) = array2d_uo is det.
% A synonym for the array2d function above.
%
:- func from_lists(list(list(T))) = array2d(T).
:- mode from_lists(in) = array2d_uo is det.
% from_array(NumRows, NumColumns, Array) constructs an array2d
% of size NumRows * NumColumns where the elements are taken from Array
% in row-major order, i.e. the element at row R column C is taken from
% Array at index (R * NumColumns + C). Indices start from zero.
%
% Throws an exception if NumRows < 0 or NumColumns < 0, or if
% the number of elements in Array does not equal NumRows * NumColumns.
%
:- func from_array(int, int, array(T)) = array2d(T).
:- mode from_array(in, in, array_di) = array2d_uo is det.
% is_empty(Array):
% True iff Array contains zero elements.
%
:- pred is_empty(array2d(T)).
% :- mode is_empty(array2d_ui) is semidet.
:- mode is_empty(in) is semidet.
% bounds(Array, NumRows, NumColumns):
%
% Returns the number of rows and columns in the given 2d array.
%
:- pred bounds(array2d(T), int, int).
% :- mode bounds(array2d_ui, out, out) is det.
:- mode bounds(in, out, out) is det.
% in_bounds(Array, R, C):
%
% Succeeds if and only if 0 =< C < NumRows, 0 =< C < NumColumns.
%
:- pred in_bounds(array2d(T), int, int).
% :- mode in_bounds(array2d_ui, in, in) is semidet.
:- mode in_bounds(in, in, in) is semidet.
% lookup(Array, R, C):
%
% Given a 2d array Array with NumRows rows and NumColumns columns,
% return the element at row R and column C. Indices start at zero.
%
% This function requires 0 =< R < NumRows and 0 =< C < NumColumns.
% If this requirement is not satisfied, this function will throw
% an exception.
%
:- func lookup(array2d(T), int, int) = T.
% :- mode lookup(array2d_ui, in, in) = out is det.
:- mode lookup(in, in, in) = out is det.
:- pred lookup(array2d(T), int, int, T).
% :- mode lookup(array2d_ui, in, in, out) is det.
:- mode lookup(in, in, in, out) is det.
:- func array2d(T) ^ elem(int, int) = T.
% :- mode array2d_ui ^ elem(in, in) = out is det.
:- mode in ^ elem(in, in) = out is det.
% unsafe_lookup(Array, R, C) = Elem:
% unsafe_lookup(Array, R, C, Elem):
% Array ^ unsafe_elem(R, C) = Elem:
%
% Given a 2d array Array with NumRows rows and NumColumns columns,
% return the element at row R and column C. Indices start at zero.
%
% This function requires 0 =< R < NumRows and 0 =< C < NumColumns.
% If this requirement is not satisfied, the behavior of this function
% is undefined.
%
:- func unsafe_lookup(array2d(T), int, int) = T.
% :- mode unsafe_lookup(array2d_ui, in, in) = out is det.
:- mode unsafe_lookup(in, in, in) = out is det.
:- pred unsafe_lookup(array2d(T), int, int, T).
% :- mode unsafe_lookup(array2d_ui, in, in, out) is det.
:- mode unsafe_lookup(in, in, in, out) is det.
:- func unsafe_elem(int, int, array2d(T)) = T.
% :- mode unsafe_elem(in, in, array2du) = out is det.
:- mode unsafe_elem(in, in, in) = out is det.
% set(R, C, NewElem, Array0, Array):
% Array0 ^ elem(R, C) := NewElem = Array:
%
% Return Array, which differs from Array0 only in that
% the value at row R and column C is NewElem.
%
% Throws an exception unless 0 =< R < NumRows, 0 =< C < NumColumns.
%
:- pred set(int, int, T, array2d(T), array2d(T)).
:- mode set(in, in, in, array2d_di, array2d_uo) is det.
:- func 'elem :='(int, int, array2d(T), T) = array2d(T).
:- mode 'elem :='(in, in, array2d_di, in) = array2d_uo is det.
% unsafe_set(R, C, NewElem, Array0) = Array:
% unsafe_set(R, C, NewElem, Array0, Array):
% Array0 ^ unsafe_elem(R, C) := NewElem = Array:
%
% Return Array, which differs from Array0 only in that
% the value at row R and column C is NewElem.
%
% The behavior is defined only if 0 =< R < NumRows, 0 =< C < NumColumns.
%
:- pred unsafe_set(int, int, T, array2d(T), array2d(T)).
:- mode unsafe_set(in, in, in, array2d_di, array2d_uo) is det.
:- func 'unsafe_elem :='(int, int, array2d(T), T) = array2d(T).
:- mode 'unsafe_elem :='(in, in, array_di, in) = array2d_uo is det.
% lists(Array):
%
% Return the contents of the given 2d array as a list of rows,
% with each row containing the values in its columns.
%
% This function is the converse of from_lists.
% For every Array, from_lists(lists(Array) = Array,
% and for every Lists for from_lists(Lists) does not throw
% an exception, lists(from_lists(Lists) = Lists.
%
:- func lists(array2d(T)) = list(list(T)).
% :- mode lists(array2d_ui) = out is det.
:- mode lists(in) = out is det.
% fill(Item, !Array):
% Sets every element of the array to Item.
%
:- pred fill(T::in, array2d(T)::array2d_di, array2d(T)::array2d_uo) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module require.
:- import_module int.
:- interface.
% This should be abstract, but needs to be exported for insts.
%
:- type array2d(T)
---> array2d(
% The number of rows.
int,
% The number of columns.
int,
% The contents of the 2d array, flattened out.
% It stores the element at row R and column C at
% index (R * NumColumns) + C in the flattened array.
array(T)
).
:- implementation.
%---------------------------------------------------------------------------%
init(NumRows, NumColumns, Elem) =
( if NumRows >= 0, NumColumns >= 0 then
array2d(NumRows, NumColumns, array.init(NumRows * NumColumns, Elem))
else
func_error($pred, "bounds must be non-negative")
).
%---------------------------------------------------------------------------%
array2d(Rows) = from_lists(Rows).
from_lists([]) = array2d(0, 0, make_empty_array).
from_lists(Rows @ [FirstRow | _]) = Array :-
NumRows = list.length(Rows),
NumColumns = list.length(FirstRow),
( if
all [Row] (
list.member(Row, Rows)
=>
list.length(Row) = NumColumns
)
then
A = array(list.condense(Rows)),
Array = array2d(NumRows, NumColumns, A)
else
error($pred, "non-rectangular list of lists")
).
from_array(NumRows, NumColumns, Array) = Array2d :-
( if
NumRows >= 0,
NumColumns >= 0
then
array.size(Array, Size),
compare(Result, Size, NumRows * NumColumns),
(
Result = (=),
Array2d = array2d(NumRows, NumColumns, Array)
;
Result = (>),
error($pred, "too many elements")
;
Result = (<),
error($pred, "too few elements")
)
else
error($pred, " bounds must be non-negative")
).
%---------------------------------------------------------------------------%
is_empty(array2d(_, _, A)) :-
array.is_empty(A).
%---------------------------------------------------------------------------%
bounds(array2d(NumRows, NumColumns, _A), NumRows, NumColumns).
%---------------------------------------------------------------------------%
in_bounds(array2d(NumRows, NumColumns, _A), R, C) :-
0 =< R, R < NumRows,
0 =< C, C < NumColumns.
%---------------------------------------------------------------------------%
lookup(Array, R, C) = Elem :-
( if in_bounds(Array, R, C) then
Elem = unsafe_lookup(Array, R, C)
else
error($pred, "indices out of bounds")
).
lookup(Array, R, C, Elem) :-
Elem = lookup(Array, R, C).
Array ^ elem(R, C) =
lookup(Array, R, C).
%---------------------------------------------------------------------------%
unsafe_lookup(Array, R, C) = Elem :-
Array = array2d(_NumRows, NumColumns, A),
array.unsafe_lookup(A, (R * NumColumns) + C, Elem).
unsafe_lookup(Array, R, C, Elem) :-
Elem = unsafe_lookup(Array, R, C).
Array ^ unsafe_elem(R, C) =
unsafe_lookup(Array, R, C).
%---------------------------------------------------------------------------%
set(R, C, Value, !Array) :-
( if in_bounds(!.Array, R, C) then
unsafe_set(R, C, Value, !Array)
else
error($pred, "indices out of bounds")
).
( Array0 ^ elem(R, C) := Value ) = Array :-
set(R, C, Value, Array0, Array).
%---------------------------------------------------------------------------%
unsafe_set(R, C, Value, !Array) :-
!.Array = array2d(NumRows, NumColumns, A0),
array.unsafe_set((R * NumColumns) + C, Value, A0, A),
!:Array = array2d(NumRows, NumColumns, A).
( Array0 ^ unsafe_elem(R, C) := Value ) = Array :-
unsafe_set(R, C, Value, Array0, Array).
%---------------------------------------------------------------------------%
lists(array2d(NumRows, NumColumns, A)) = Rows :-
get_rows(NumRows - 1, NumColumns, A, [], Rows).
:- pred get_rows(int, int, array(T), list(list(T)), list(list(T))).
% :- mode get_rows(in, in, array_ui, in, out) is det.
:- mode get_rows(in, in, in, in, out) is det.
get_rows(RowNum, NumColumns, A, !Rows) :-
( if RowNum >= 0 then
get_columns(RowNum, NumColumns - 1, NumColumns, A, [], Columns),
!:Rows = [Columns | !.Rows],
get_rows(RowNum - 1, NumColumns, A, !Rows)
else
true
).
:- pred get_columns(int, int, int, array(T), list(T), list(T)).
% :- mode get_columns(in, in, in, array_ui, in, out) is det.
:- mode get_columns(in, in, in, in, in, out) is det.
get_columns(RowNum, ColumnNum, NumColumns, A, !Columns) :-
( if ColumnNum >= 0 then
array.unsafe_lookup(A, (RowNum * NumColumns) + ColumnNum, Elem),
!:Columns = [Elem | !.Columns],
get_columns(RowNum, ColumnNum - 1, NumColumns, A, !Columns)
else
true
).
%---------------------------------------------------------------------------%
fill(Item, Array0, Array) :-
Array0 = array2d(NumRows, NumColumns, A0),
array.fill(Item, A0, A),
Array = array2d(NumRows, NumColumns, A).
%---------------------------------------------------------------------------%
:- end_module array2d.
%---------------------------------------------------------------------------%