[m-dev.] diff: Array reorganisation

Andrew Bromage bromage at cs.mu.oz.au
Fri Jul 25 14:47:32 AEST 1997


G'day.

This is a diff between the old submission and the new one.  Yes, the
change in actual_position/4 is a bug fix.

Cheers,
Andrew Bromage


Estimated hours taken: 7

The main purpose of this change is to rename array.m as bt_array.m, and
uniq_array.m as array.m.  The interfaces of those two modules have grown
slightly so that they match a little more closely.  Details are in the
file NEWS.

The implementation of bt_array (formerly array) has been changed to use
a slightly more efficient implementation.

NEWS:
	Interface changes documented.

library/array.m:
library/bt_array.m:
	Changes mentioned above and detailed in the NEWS file.

library/uniq_array.m:
	Bereft of life and resting in peace.

library/io.m:
library/library.m:
library/std_util.m:
library/term.m:
compiler/base_type_layout.m:
runtime/deep_copy.c:
runtime/type_info.h:
	Minor changes to fix the special case of base_type_layout
	operations for arrays rather than uniq_arrays.

tests/hard_coded/write.exp:
tests/hard_coded/write.m:
	Test writing of arrays.

tests/general/array_test.exp:
tests/general/array_test.m:
	Test some array/bt_array operations.


Index: library/bt_array.m
===================================================================
--- bt_array_old.m	Fri Jul 25 14:37:43 1997
+++ bt_array.m	Fri Jul 25 14:19:42 1997
@@ -11,7 +11,14 @@
 % This file contains a set of predicates for generating an manipulating
 % a bt_array data structure.  This implementation allows O(log n) access
 % and update time, and does not require the bt_array to be unique.  If you
-% need O(1) access/update time, use the uniq_bt_array datatype instead.
+% need O(1) access/update time, use the array datatype instead.
+
+% Implementation obscurity: This implementation is biassed towards larger
+% indices.  The access/update time for a bt_array of size N with index I
+% is actually O(log(N-I)).  The reason for this is so that the resize
+% operations can be optimised for a (possibly very) common case, and to
+% exploit accumulator recursion in some operations.  See the documentation
+% of bt_array__resize and bt_array__shrink for more details.
 
 %-----------------------------------------------------------------------------%
 %-----------------------------------------------------------------------------%
@@ -87,6 +94,13 @@
 	% BtArray0 to fit the bounds (Lo,Hi).  If the new bounds are
 	% not wholly contained within the bounds of BtArray0, Item is
 	% used to fill out the other places.
+	%
+	% Note: This operation is optimised for the case where the
+	% lower bound of the new bt_array is the same as that of
+	% the old bt_array.  In that case, the operation takes time
+	% proportional to the absolute difference in size between
+	% the two bt_arrays.  If this is not the case, it may take
+	% time proportional to the larger of the two bt_arrays.
 :- pred bt_array__resize(bt_array(T), int, int, T, bt_array(T)).
 :- mode bt_array__resize(in, in, in, in, out) is det.
 
@@ -94,6 +108,13 @@
 	% if BtArray is a bt_array created by shrinking BtArray0 to
 	% fit the bounds (Lo,Hi).  It is an error if the new bounds
 	% are not wholly within the bounds of BtArray0.
+	%
+	% Note: This operation is optimised for the case where the
+	% lower bound of the new bt_array is the same as that of
+	% the old bt_array.  In that case, the operation takes time
+	% proportional to the absolute difference in size between
+	% the two bt_arrays.  If this is not the case, it may take
+	% time proportional to the larger of the two bt_arrays.
 :- pred bt_array__shrink(bt_array(T), int, int, bt_array(T)).
 :- mode bt_array__shrink(in, in, in, out) is det.
 
@@ -136,8 +157,8 @@
 
 %-----------------------------------------------------------------------------%
 
-bt_array__make_empty_array(Low, bt_array(Low, Hi, ListOut)) :-
-	Hi is Low - 1,
+bt_array__make_empty_array(Low, bt_array(Low, High, ListOut)) :-
+	High is Low - 1,
 	ra_list_nil(ListOut).
 
 bt_array__init(Low, High, Item, bt_array(Low, High, ListOut)) :-
@@ -178,7 +199,7 @@
 :- mode actual_position(in, in, in, out) is det.
 
 actual_position(Low, High, Index, Pos) :-
-	Pos is High - Low - Index + 1.
+	Pos is High - Low - Index.
 
 bt_array__lookup(bt_array(Low, High, RaList), Index, Item) :-
 	actual_position(Low, High, Index, Pos),
@@ -346,6 +367,8 @@
 	Lo =< Hi,
 	bt_array__bsearch_2(A, Lo, Hi, El, Compare, I).
 
+	% XXX Would we gain anything by traversing the ra_list instead
+	%     of doing a vanilla binary chop?
 :- pred bt_array__bsearch_2(bt_array(T), int, int, T,
 			pred(T, T, comparison_result), int).
 :- mode bt_array__bsearch_2(in, in, in, in, pred(in, in, out) is det,
@@ -393,6 +416,18 @@
 % This is a perfect application for submodules, but Mercury doesn't have
 % them. :-(
 
+% The heart of the implementation of bt_array is a `random access list'
+% or ra_list for short.  It is very similar to a list data type, and
+% it supports O(1) head/tail/cons operations, but O(log n) lookup and
+% update.  The representation is a list of perfectly balanced forest
+% of binary trees.
+%
+% For mode details on the implementation:
+%
+%	Chris Okasaki, "Purely Functional Random-Access Lists"
+%	Functional Programming Languages and Computer Architecutre,
+%	June 1995, pp 86-95.
+
 % :- module ra_list.
 % :- interface.
 
@@ -453,7 +488,8 @@
 		List0 = cons(Size1, T1, cons(Size2, T2, Rest)),
 		Size1 = Size2
 	->
-		List = cons(1+Size1+Size2, node(X, T1, T2), Rest)
+		NewSize is 1 + Size1 + Size2,
+		List = cons(NewSize, node(X, T1, T2), Rest)
 	;
 		List = cons(1, leaf(X), List0)
 	).
@@ -479,7 +515,7 @@
 
 %-----------------------------------------------------------------------------%
 
-:- pragma inline(ra_list_update/4).
+:- pragma inline(ra_list_lookup/3).
 
 ra_list_lookup(I, List, X) :-
 	I >= 0,
@@ -505,9 +541,11 @@
 	;
 		Size2 = Size // 2,
 		( I =< Size2 ->
-			ra_list_bintree_lookup(Size2, T1, I-1, X)
+			NewI is I - 1,
+			ra_list_bintree_lookup(Size2, T1, NewI, X)
 		;
-			ra_list_bintree_lookup(Size2, T2, I-1-Size2, X)
+			NewI is I - 1 - Size2,
+			ra_list_bintree_lookup(Size2, T2, NewI, X)
 		)
 	).
 
@@ -542,10 +580,12 @@
 	;
 		Size2 = Size // 2,
 		( I =< Size2 ->
-			ra_list_bintree_update(Size2, T1, I-1, X, T0),
+			NewI is I - 1,
+			ra_list_bintree_update(Size2, T1, NewI, X, T0),
 			T = node(X0, T0, T2)
 		;
-			ra_list_bintree_update(Size2, T2, I-1-Size2, X, T0),
+			NewI is I - 1 - Size2,
+			ra_list_bintree_update(Size2, T2, NewI, X, T0),
 			T = node(X0, T1, T0)
 		)
 	).

Index: tests/general/Mmake
===================================================================
RCS file: /home/staff/zs/imp/tests/general/Mmake,v
retrieving revision 1.46
diff -u -r1.46 Mmake
--- Mmake	1997/07/23 23:25:40	1.46
+++ Mmake	1997/07/25 02:42:09
@@ -13,7 +13,8 @@
 
 #-----------------------------------------------------------------------------#
 
-PROGS=		commit_bug \
+PROGS=		array_test \
+		commit_bug \
 		commit_bug_2 \
 		disj_disj \
 		dnf \
New file: tests/general/array_test.m
===================================================================
% Some checks of array implementation.

:- module array_test.
:- interface.
:- import_module io.

:- pred main(io__state, io__state).
:- mode main(di, uo) is det.

:- implementation.
:- import_module int, string, list, array, bt_array, std_util.

main -->
	test([1,2,3,4,5,6,7,8,9,10]).

:- pred test(list(int), io__state, io__state).
:- mode test(in, di, uo) is det.

test(Xs) -->
	{
		Cmp = lambda([X :: in, Y :: in, Res :: out] is det,
			compare(Res, X, Y)),
		A0 = array(Xs),
		array__to_list(A0, As0),
		array__max(A0, AMax0),
		array__min(A0, AMin0),
		array__size(A0, ASize),
		array__bounds(A0, AMin1, AMax1),
		array__bsearch(A0, 4, Cmp, ABsearch),
		array__set(A0, 8, 100, A1),
		array__to_list(A1, As1),
		array__resize(A1, 15, 1000, A2),
		array__to_list(A2, As2),
		array__shrink(A2, 10, A3),
		array__to_list(A3, As3)
	},
	write_message("A0: ", As0),
	write_message("AMax0: ", AMax0),
	write_message("AMin0: ", AMin0),
	write_message("ASize: ", ASize),
	write_message("AMin1: ", AMin1),
	write_message("AMax1: ", AMax1),
	write_message("ABsearch: ", ABsearch),
	write_message("A1: ", As1),
	write_message("A2: ", As2),
	write_message("A3: ", As3),
	{
		bt_array__from_list(0, Xs, B0),
		bt_array__to_list(B0, Bs0),
		bt_array__max(B0, BMax0),
		bt_array__min(B0, BMin0),
		bt_array__size(B0, BSize),
		bt_array__bounds(B0, BMin1, BMax1),
		( bt_array__bsearch(B0, 4, Cmp, BBsearch0) ->
			BBsearch = yes(BBsearch0)
		;
			BBsearch = no
		),
		bt_array__set(B0, 8, 100, B1),
		bt_array__to_list(B1, Bs1),
		bt_array__resize(B1, 0, 15, 1000, B2),
		bt_array__to_list(B2, Bs2),
		bt_array__shrink(B2, 0, 10, B3),
		bt_array__to_list(B3, Bs3)
	},
	write_message("B0: ", Bs0),
	write_message("BMax0: ", BMax0),
	write_message("BMin0: ", BMin0),
	write_message("BSize: ", BSize),
	write_message("BMin1: ", BMin1),
	write_message("BMax1: ", BMax1),
	write_message("BBsearch: ", BBsearch),
	write_message("B1: ", Bs1),
	write_message("B2: ", Bs2),
	write_message("B3: ", Bs3),
	
	% Finally, just in case, compare the two implementations.
	(
		{
			As0 = Bs0,
			AMax0 = BMax1,
			AMin0 = BMin1,
			ASize = BSize,
			AMin1 = BMin1,
			AMax1 = BMax1,
			AMax0 = AMax1,
			AMin0 = AMin1,
			BMax0 = BMax1,
			BMin0 = BMin1,
			ABSearch = BBSearch,
			As1 = Bs1,
			As2 = Bs2,
			As3 = Bs3,
			As1 = As3,
			Bs1 = Bs3
		}
	->
		io__write_string("Results all match")
	;
		io__write_string("Results don't match")
	).

:- pred write_message(string, T, io__state, io__state).
:- mode write_message(in, array_ui, di, uo) is det.
:- mode write_message(in, in, di, uo) is det.

write_message(Msg, O) -->
	io__write_string(Msg),
	io__write(O),
	io__nl.

New file: tests/general/array_test.exp
===================================================================
A0: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
AMax0: 9
AMin0: 0
ASize: 10
AMin1: 0
AMax1: 9
ABsearch: yes(3)
A1: [1, 2, 3, 4, 5, 6, 7, 8, 100, 10]
A2: [1, 2, 3, 4, 5, 6, 7, 8, 100, 10, 1000, 1000, 1000, 1000, 1000]
A3: [1, 2, 3, 4, 5, 6, 7, 8, 100, 10]
B0: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
BMax0: 9
BMin0: 0
BSize: 10
BMin1: 0
BMax1: 9
BBsearch: yes(3)
B1: [1, 2, 3, 4, 5, 6, 7, 8, 100, 10]
B2: [1, 2, 3, 4, 5, 6, 7, 8, 100, 10, 1000, 1000, 1000, 1000, 1000]
B3: [1, 2, 3, 4, 5, 6, 7, 8, 100, 10]
Results all match



More information about the developers mailing list