mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
376 lines
12 KiB
Mathematica
376 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-2026 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.
|
|
%
|
|
% 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 if-and-only-if 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 =< R < 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 which 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 int.
|
|
:- import_module require.
|
|
|
|
:- 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).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma inline(pred(in_bounds/3)).
|
|
|
|
in_bounds(array2d(NumRows, NumColumns, _A), R, C) :-
|
|
private_builtin.in_range(R, NumRows),
|
|
private_builtin.in_range(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.
|
|
%---------------------------------------------------------------------------%
|