%   File   : LOGARR.PL
%   Author : Mostly David H.D.Warren, some changes by Fernando Pereira
%   Updated: 24 September 1984
%   Purpose: Extendable arrays with logarithmic access time.

/*  LOGARITHMIC ARRAYS.

    An array extends from 0 to 2**Size - 1, where Size is a multiple of 2.
    Note that 2**Size = 1<External interface.

    new_array(A) returns a new empty array A.

    is_array(A) checks whether A is an array.

    aref(Index,Array,Element) unifies Element with Array[Index],
	or fails if Array[Index] has not been set.

    arefa(Index,Array,Element) is as aref/3, except that it unifies
	Element with a new array if Array[Index] is undefined.
	This is useful for multidimensional arrays implemented
	as arrays of arrays.

    arefl(Index,Array,Element) is as aref/3, except that Element
	appears as '[]' for undefined cells.

    aset(Index,Array,Element,NewArray) unifies NewArray with the
	result of setting Array[Index] to Element.

    array_to_list(Array,List) returns a list of pairs Index-Element
	of all the elements of Array that have been set.

	In the interests of uniformity, R.A.O'K used the Prolog source
	code tidier on this file; this is not its original layout.  He
	made no algorithmic changes, however.
*/

:- public
	new_array/1,
	is_array/1,
	aref/3,
	arefa/3,
	arefl/3,
	aset/4,
	array_to_list/2.

:- mode
	aref(+, +, ?),
	arefa(+, +, ?),
	arefl(+, +, ?),
	array_to_list(+, -),
	aset(+, +, +, -),
	array_item(+, +, +, +, ?),
	is_array(+),
	new_array(-),
	not_undef(+),
	subarray(+, +, ?),
	subarray_to_list(+, +, +, +, ?, ?),
	update_subarray(+, +, ?, ?, -).


new_array(array($($,$,$,$),2)).


is_array(array(_,_)).


aref(Index, array(Array,Size), Item) :-
	check_int(Index),
	Index < 1<>N /\ 3,
	array_item(Subindex, N, Index, Array, Item).


array_to_list(array($(A0,A1,A2,A3),Size), L0) :-
	N is Size-2,
	subarray_to_list(0, N, 0, A0, L0, L1),
	subarray_to_list(1, N, 0, A1, L1, L2),
	subarray_to_list(2, N, 0, A2, L2, L3),
	subarray_to_list(3, N, 0, A3, L3, []).


arefa(Index, array(Array,Size), Item) :-
	check_int(Index),
	Index < 1<>N /\ 3,
	array_item(Subindex, N, Index, Array, Item), !.
arefa(_, _, Item) :-
	new_array(Item).


arefl(Index, array(Array,Size), Item) :-
	check_int(Index),
	Index < 1<>N /\ 3,
	array_item(Subindex, N, Index, Array, Item), !.
arefl(_, _, []).


aset(Index, array(Array0,Size0), Item, array(Array,Size)) :-
	check_int(Index),
	enlarge_array(Index, Size0, Array0, Size, Array1),
	update_array_item(Size, Index, Array1, Item, Array).


check_int(I) :-
	integer(I), !.
check_int(X) :-
	write('Array index not integer: '), write(X), nl,
	trace, fail.

% Guts

enlarge_array(I, Size, Array, Size, Array) :-
	I < 1<> N1 /\ 3,
	array_item(Subindex, N1, Index, Array, Item).
array_item(1, 0, _, $(_,Item,_,_), Item) :- !,
	not_undef(Item).
array_item(1, N, Index, $(_,Array,_,_), Item) :-
	N1 is N-2,
	Subindex is Index >> N1 /\ 3,
	array_item(Subindex, N1, Index, Array, Item).
array_item(2, 0, _, $(_,_,Item,_), Item) :- !,
	not_undef(Item).
array_item(2, N, Index, $(_,_,Array,_), Item) :-
	N1 is N-2,
	Subindex is Index >> N1 /\ 3,
	array_item(Subindex, N1, Index, Array, Item).
array_item(3, 0, _, $(_,_,_,Item), Item) :- !,
	not_undef(Item).
array_item(3, N, Index, $(_,_,_,Array), Item) :-
	N1 is N-2,
	Subindex is Index >> N1 /\ 3,
	array_item(Subindex, N1, Index, Array, Item).


not_undef($) :- !,
	fail.
not_undef(_).


%% [BEFORE OPEN-CODING 'subarray']
%%
%% array_item(0,Index,Item,Item) :- !,
%%	not_undef(Item).
%% array_item(N,Index,Array,Item) :-
%%	N1 is N-2,
%%	Subindex is Index >> N1 /\ 3,
%%	subarray(Subindex,Array,Array1),
%%	array_item(N1,Index,Array1,Item).
%%
%% subarray(0,$(X,_,_,_),X).
%% subarray(1,$(_,X,_,_),X).
%% subarray(2,$(_,_,X,_),X).
%% subarray(3,$(_,_,_,X),X).

update_array_item(0, _, _, NewItem, NewItem) :- !.
update_array_item(N, Index, Array, NewItem, NewArray) :-
	N1 is N-2,
	Subindex is Index >> N1 /\ 3,
	update_subarray(Subindex, Array, Array1, NewArray1, NewArray),
	update_array_item(N1, Index, Array1, NewItem, NewArray1).


update_subarray(I, $, X, X1, Array) :- !,
	update_subarray(I, $($,$,$,$), X, X1, Array).
update_subarray(0, $(W,X,Y,Z), W, W1, $(W1,X,Y,Z)).
update_subarray(1, $(W,X,Y,Z), X, X1, $(W,X1,Y,Z)).
update_subarray(2, $(W,X,Y,Z), Y, Y1, $(W,X,Y1,Z)).
update_subarray(3, $(W,X,Y,Z), Z, Z1, $(W,X,Y,Z1)).


subarray_to_list(K, 0, M, Item, [N-Item|L], L) :-
	not_undef(Item), !,
	N is K+M.
subarray_to_list(K, N, M, $(A0,A1,A2,A3), L0, L) :-
	N > 0, !,
	N1 is N-2,
	M1 is (K+M) << 2,
	subarray_to_list(0, N1, M1, A0, L0, L1),
	subarray_to_list(1, N1, M1, A1, L1, L2),
	subarray_to_list(2, N1, M1, A2, L2, L3),
	subarray_to_list(3, N1, M1, A3, L3, L).
subarray_to_list(_, _, _, _, L, L).