mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
810 lines
27 KiB
Mathematica
810 lines
27 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1997, 2000, 2003, 2005-2006 The University of Melbourne.
|
|
% Copyright (C) 2014-2015, 2021-2026 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: ra_list.m.
|
|
% Main author: bromage.
|
|
% Stability: medium.
|
|
%
|
|
% This module implements `random access lists', or ra_lists for short.
|
|
% It is very similar to a list data type, and it supports O(1) head/tail/cons
|
|
% operations, but it also supports O(log n) lookup and update.
|
|
% The representation is a list of perfectly balanced binary trees.
|
|
%
|
|
% For more details on the implementation:
|
|
%
|
|
% Chris Okasaki, "Purely Functional Random-Access Lists".
|
|
% Functional Programming Languages and Computer Architecture,
|
|
% June 1995, pp 86-95.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ra_list.
|
|
:- interface.
|
|
|
|
:- import_module list.
|
|
|
|
:- type ra_list(T).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Constructing ra_lists.
|
|
%
|
|
|
|
% Return an empty random access list.
|
|
%
|
|
:- pred init(ra_list(T)::uo) is det.
|
|
|
|
% Return a random access list containing only the given item.
|
|
%
|
|
:- func singleton(T) = ra_list(T).
|
|
|
|
% Return a random access list with the given head and tail.
|
|
%
|
|
:- pred cons(T::in, ra_list(T)::in, ra_list(T)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Deconstructing ra_lists.
|
|
%
|
|
|
|
% Return the head of the given random access list.
|
|
% Fail if it is empty.
|
|
%
|
|
:- pred head(ra_list(T)::in, T::out) is semidet.
|
|
|
|
% Return the tail of the given random access list.
|
|
% Fail if it is empty.
|
|
%
|
|
:- pred tail(ra_list(T)::in, ra_list(T)::out) is semidet.
|
|
|
|
% Return the head and the tail of the given random access list.
|
|
% Fail if it is empty.
|
|
%
|
|
:- pred head_tail(ra_list(T)::in, T::out, ra_list(T)::out) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Tests on ra_lists.
|
|
%
|
|
|
|
% Succeed if-and-only-if the given random access list is empty.
|
|
%
|
|
:- pred is_empty(ra_list(T)::in) is semidet.
|
|
|
|
% Succeed if-and-only-if the given random access list is not empty.
|
|
%
|
|
:- pred is_non_empty(ra_list(T)::in) is semidet.
|
|
:- pred is_not_empty(ra_list(T)::in) is semidet.
|
|
|
|
% Succeed if-and-only-if the given random access list contains
|
|
% only one item. Return that item.
|
|
%
|
|
:- pred is_singleton(ra_list(T)::in, T::out) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Counting items in ra_lists.
|
|
%
|
|
|
|
% Return the number of items in the given random access list.
|
|
%
|
|
:- func length(ra_list(T)) = int.
|
|
:- pred length(ra_list(T)::in, int::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Random access on ra_lists.
|
|
%
|
|
|
|
% Return the item at the given index in the given random access list.
|
|
% The number at the end of the predicate name gives the index of the first
|
|
% element.
|
|
%
|
|
% Fail if the list is not long enough to have an element
|
|
% at the given index.
|
|
%
|
|
:- pred index0(ra_list(T)::in, int::in, T::out) is semidet.
|
|
:- pred index1(ra_list(T)::in, int::in, T::out) is semidet.
|
|
|
|
% Return the item at the given index in the given random access list.
|
|
% The number at the end of the predicate name gives the index of the first
|
|
% element.
|
|
%
|
|
% Fail if the list is not long enough to have an element
|
|
% at the given index.
|
|
%
|
|
:- pred det_index0(ra_list(T)::in, int::in, T::out) is det.
|
|
:- pred det_index1(ra_list(T)::in, int::in, T::out) is det.
|
|
|
|
% Replace the item at the given index in the given random access list.
|
|
% The first element is at index 0.
|
|
%
|
|
% Fail if the list is not long enough to have an element
|
|
% at the given index.
|
|
%
|
|
:- pred update(int::in, T::in, ra_list(T)::in, ra_list(T)::out) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Append two random access lists.
|
|
%
|
|
:- pred append(ra_list(T)::in, ra_list(T)::in, ra_list(T)::out) is det.
|
|
|
|
% Drop the given number of initial items from the given random access list.
|
|
%
|
|
% Return the list unchanged if the number of elements to drop is zero
|
|
% or negative.
|
|
%
|
|
% Fail if the list does not have at least that number of elements.
|
|
%
|
|
:- pred drop(int::in, ra_list(T)::in, ra_list(T)::out) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Convert a list to a random access list.
|
|
%
|
|
:- pred list_to_ra_list(list(T)::in, ra_list(T)::out) is det.
|
|
|
|
% Convert a random access list to a plain list.
|
|
%
|
|
:- pred ra_list_to_list(ra_list(T)::in, list(T)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% map(F, L) = M:
|
|
% map(P, L, M):
|
|
%
|
|
% Apply the function F or predicate P to transform the elements of L
|
|
% into the elements of M.
|
|
%
|
|
:- func map(func(T1) = T2, ra_list(T1)) = ra_list(T2).
|
|
:- pred map(pred(T1, T2), ra_list(T1), ra_list(T2)).
|
|
:- mode map(in(pred(in, out) is det), in, out) is det.
|
|
:- mode map(in(pred(in, out) is cc_multi), in, out) is cc_multi.
|
|
:- mode map(in(pred(in, out) is semidet), in, out) is semidet.
|
|
:- mode map(in(pred(in, out) is multi), in, out) is multi.
|
|
:- mode map(in(pred(in, out) is nondet), in, out) is nondet.
|
|
:- mode map(in(pred(in, in) is semidet), in, in) is semidet.
|
|
|
|
% foldl(Func, List, Start) = End:
|
|
% foldl(Pred, List, Start, End):
|
|
%
|
|
% Calls Func or Pred on each element of List, working left-to-right.
|
|
% Each call to Func or Pred will have a pair of arguments that represent
|
|
% respectively the current and the next value of a piece of state.
|
|
% (Such current-next argument pairs are usually called an accumulator,
|
|
% because the usual use case is that the successive calls to Func or Pred
|
|
% accumulate pieces of information.) The initial value of the accumulator
|
|
% is Start, each call to Func or Pred updates it to the next value, and
|
|
% foldl returns its final value as End.
|
|
%
|
|
:- func foldl(func(T, A) = A, ra_list(T), A) = A.
|
|
:- pred foldl(pred(T, A, A), ra_list(T), A, A).
|
|
:- mode foldl(in(pred(in, in, out) is det), in, in, out) is det.
|
|
:- mode foldl(in(pred(in, mdi, muo) is det), in, mdi, muo) is det.
|
|
:- mode foldl(in(pred(in, di, uo) is det), in, di, uo) is det.
|
|
:- mode foldl(in(pred(in, in, out) is semidet), in, in, out) is semidet.
|
|
:- mode foldl(in(pred(in, mdi, muo) is semidet), in, mdi, muo) is semidet.
|
|
:- mode foldl(in(pred(in, di, uo) is semidet), in, di, uo) is semidet.
|
|
:- mode foldl(in(pred(in, in, out) is multi), in, in, out) is multi.
|
|
:- mode foldl(in(pred(in, in, out) is nondet), in, in, out) is nondet.
|
|
:- mode foldl(in(pred(in, mdi, muo) is nondet), in, mdi, muo) is nondet.
|
|
:- mode foldl(in(pred(in, in, out) is cc_multi), in, in, out) is cc_multi.
|
|
:- mode foldl(in(pred(in, di, uo) is cc_multi), in, di, uo) is cc_multi.
|
|
|
|
% foldr(Func, List, Start) = End:
|
|
% foldr(Pred, List, Start, End):
|
|
%
|
|
% Calls Func or Pred on each element of List, working right-to-left.
|
|
% Each call to Func or Pred will have a pair of arguments that represent
|
|
% respectively the current and the next value of a piece of state.
|
|
% (Such current-next argument pairs are usually called an accumulator,
|
|
% because the usual use case is that the successive calls to Func or Pred
|
|
% accumulate pieces of information.) The initial value of the accumulator
|
|
% is Start, each call to Func or Pred updates it to the next value, and
|
|
% foldr returns its final value as End.
|
|
%
|
|
:- func foldr(func(T, A) = A, ra_list(T), A) = A.
|
|
:- pred foldr(pred(T, A, A), ra_list(T), A, A).
|
|
:- mode foldr(in(pred(in, in, out) is det), in, in, out) is det.
|
|
:- mode foldr(in(pred(in, mdi, muo) is det), in, mdi, muo) is det.
|
|
:- mode foldr(in(pred(in, di, uo) is det), in, di, uo) is det.
|
|
:- mode foldr(in(pred(in, in, out) is semidet), in, in, out) is semidet.
|
|
:- mode foldr(in(pred(in, mdi, muo) is semidet), in, mdi, muo) is semidet.
|
|
:- mode foldr(in(pred(in, di, uo) is semidet), in, di, uo) is semidet.
|
|
:- mode foldr(in(pred(in, in, out) is multi), in, in, out) is multi.
|
|
:- mode foldr(in(pred(in, in, out) is nondet), in, in, out) is nondet.
|
|
:- mode foldr(in(pred(in, mdi, muo) is nondet), in, mdi, muo) is nondet.
|
|
:- mode foldr(in(pred(in, di, uo) is cc_multi), in, di, uo) is cc_multi.
|
|
:- mode foldr(in(pred(in, in, out) is cc_multi), in, in, out) is cc_multi.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module int.
|
|
:- import_module require.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Okasaki's paper represents random access lists as a list of
|
|
% <bintree_size, bintree> pairs. We use a fat-list-style variation
|
|
% of that representation, in which a single three-argument cons cell
|
|
% replaces both a standard two-argument cons cell and a pair cell
|
|
% in Okasaki's representation. This requires fewer memory allocations
|
|
% when constructing ra_lists, and fewer dependent loads when traversing
|
|
% existing ra_lists. (It may also require less memory, though with Boehm,
|
|
% each three-argument cons cell requires a four word block.)
|
|
%
|
|
:- type ra_list(T)
|
|
---> nil
|
|
% The list is empty.
|
|
; cons(int, ra_list_bintree(T), ra_list(T)).
|
|
% cons(Size, BinTree, Rest):
|
|
%
|
|
% The list contains the elements of BinTree, followed by
|
|
% the elements of Rest. Each BinTree must be a perfect balanced
|
|
% binary tree, which means that all their levels, including their
|
|
% lowest levels, must be completely filled. The Size in each cons
|
|
% gives the number of elements in the BinTree next to it.
|
|
%
|
|
% Due to BinTrees being perfectly balanced, each Size must have
|
|
% the form (2^k) - 1; Okasaki calls such integers "skew-binary
|
|
% terms".
|
|
%
|
|
% With one exception, every ra_list_bintree in the third field
|
|
% of a cons must be strictly bigger than the ra_list_bintree
|
|
% in the second field. The exception is that the first two cons
|
|
% cells in an ra_list may contain bintrees of equal size.
|
|
|
|
:- type ra_list_bintree(T)
|
|
---> leaf(T)
|
|
% This tree contains only this item.
|
|
; node(T, ra_list_bintree(T), ra_list_bintree(T)).
|
|
% node(I, TL, TR):
|
|
% This tree contains item I, followed by the items in TL,
|
|
% followed by the items in TR. In other words, the order of
|
|
% the items in the tree is given by the *preorder* traversal of
|
|
% the binary tree, not the usual *inorder* traversal.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma inline(pred(init/1)).
|
|
|
|
init(nil).
|
|
|
|
:- pragma inline(func(singleton/1)).
|
|
|
|
singleton(X) = cons(1, leaf(X), nil).
|
|
|
|
:- pragma inline(pred(cons/3)).
|
|
|
|
cons(X, List0, List) :-
|
|
( if
|
|
List0 = cons(Size1, T1, cons(Size2, T2, Rest)),
|
|
Size1 = Size2
|
|
then
|
|
NewSize = 1 + Size1 + Size2,
|
|
List = cons(NewSize, node(X, T1, T2), Rest)
|
|
else
|
|
List = cons(1, leaf(X), List0)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma inline(pred(head/2)).
|
|
|
|
head(cons(_, leaf(X), _), X).
|
|
head(cons(_, node(X, _, _), _), X).
|
|
|
|
:- pragma inline(pred(tail/2)).
|
|
|
|
tail(cons(_, leaf(_), Tail), Tail).
|
|
tail(cons(Size, node(_, T1, T2), Rest), Tail) :-
|
|
Size2 = Size // 2,
|
|
Tail = cons(Size2, T1, cons(Size2, T2, Rest)).
|
|
|
|
:- pragma inline(pred(head_tail/3)).
|
|
|
|
head_tail(cons(_, leaf(X), Tail), X, Tail).
|
|
head_tail(cons(Size, node(X, T1, T2), Rest), X, Tail) :-
|
|
Size2 = Size // 2,
|
|
Tail = cons(Size2, T1, cons(Size2, T2, Rest)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
is_empty(RA) :-
|
|
RA = nil.
|
|
|
|
is_non_empty(RA) :-
|
|
RA = cons(_, _, _).
|
|
|
|
is_not_empty(RA) :-
|
|
RA = cons(_, _, _).
|
|
|
|
is_singleton(RA, X) :-
|
|
RA = cons(1, leaf(X), nil).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
length(RAList) = Len :-
|
|
length_acc(RAList, 0, Len).
|
|
|
|
length(RAList, Len) :-
|
|
length_acc(RAList, 0, Len).
|
|
|
|
:- pred length_acc(ra_list(T)::in, int::in, int::out) is det.
|
|
|
|
length_acc(nil, !Len).
|
|
length_acc(cons(BinTreeSize, _BinTree, Tail), !Len) :-
|
|
!:Len = !.Len + BinTreeSize,
|
|
length_acc(Tail, !Len).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma inline(pred(index0/3)).
|
|
|
|
index0(List, I0, X) :-
|
|
I0 >= 0,
|
|
index0_list(List, I0, X).
|
|
|
|
:- pragma inline(pred(index1/3)).
|
|
|
|
index1(List, I1, X) :-
|
|
I0 = I1 - 1,
|
|
I0 >= 0,
|
|
index0_list(List, I0, X).
|
|
|
|
:- pred index0_list(ra_list(T)::in, int::in, T::out) is semidet.
|
|
|
|
index0_list(cons(Size, T, Rest), I, X) :-
|
|
( if I < Size then
|
|
index0_bintree(Size, T, I, X)
|
|
else
|
|
NewI = I - Size,
|
|
index0_list(Rest, NewI, X)
|
|
).
|
|
|
|
:- pred index0_bintree(int::in, ra_list_bintree(T)::in, int::in,
|
|
T::out) is semidet.
|
|
|
|
index0_bintree(_, leaf(X), 0, X).
|
|
index0_bintree(Size, node(X0, T1, T2), I, X) :-
|
|
( if I = 0 then
|
|
X = X0
|
|
else
|
|
Size2 = Size // 2,
|
|
( if I =< Size2 then
|
|
NewI = I - 1,
|
|
index0_bintree(Size2, T1, NewI, X)
|
|
else
|
|
NewI = I - 1 - Size2,
|
|
index0_bintree(Size2, T2, NewI, X)
|
|
)
|
|
).
|
|
|
|
det_index0(List, I, X) :-
|
|
( if index0(List, I, XPrime) then
|
|
X = XPrime
|
|
else
|
|
unexpected($pred, "index0 failed")
|
|
).
|
|
|
|
det_index1(List, I, X) :-
|
|
( if index1(List, I, XPrime) then
|
|
X = XPrime
|
|
else
|
|
unexpected($pred, "index1 failed")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma inline(pred(update/4)).
|
|
|
|
update(I, X, List0, List) :-
|
|
I >= 0,
|
|
update_2(I, X, List0, List).
|
|
|
|
:- pred update_2(int::in, T::in, ra_list(T)::in, ra_list(T)::out)
|
|
is semidet.
|
|
|
|
update_2(I, X, List0, List) :-
|
|
List0 = cons(Size, T0, Rest),
|
|
( if I < Size then
|
|
update_bintree(Size, I, X, T0, T),
|
|
List = cons(Size, T, Rest)
|
|
else
|
|
NewI = I - Size,
|
|
update_2(NewI, X, Rest, List0),
|
|
List = cons(Size, T0, List0)
|
|
).
|
|
|
|
:- pred update_bintree(int::in, int::in, T::in,
|
|
ra_list_bintree(T)::in, ra_list_bintree(T)::out) is semidet.
|
|
|
|
update_bintree(_, 0, X, leaf(_), leaf(X)).
|
|
update_bintree(Size, I, X, node(X0, T1, T2), T) :-
|
|
( if I = 0 then
|
|
T = node(X, T1, T2)
|
|
else
|
|
Size2 = Size // 2,
|
|
( if I =< Size2 then
|
|
NewI = I - 1,
|
|
update_bintree(Size2, NewI, X, T1, T0),
|
|
T = node(X0, T0, T2)
|
|
else
|
|
NewI = I - 1 - Size2,
|
|
update_bintree(Size2, NewI, X, T2, T0),
|
|
T = node(X0, T1, T0)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
append(As, Bs, ABs) :-
|
|
( if head_tail(As, AH, AT) then
|
|
append(AT, Bs, ATBs),
|
|
cons(AH, ATBs, ABs)
|
|
else
|
|
ABs = Bs
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
drop(N, As, Bs) :-
|
|
( if N > 0 then
|
|
As = cons(Size, _, Cs),
|
|
( if Size < N then
|
|
N1 = N - Size,
|
|
drop(N1, Cs, Bs)
|
|
else
|
|
slow_drop(N, As, Bs)
|
|
)
|
|
else
|
|
As = Bs
|
|
).
|
|
|
|
:- pred slow_drop(int::in, ra_list(T)::in, ra_list(T)::out) is semidet.
|
|
|
|
slow_drop(N, As, Bs) :-
|
|
( if N > 0 then
|
|
N1 = N - 1,
|
|
tail(As, Cs),
|
|
slow_drop(N1, Cs, Bs)
|
|
else
|
|
As = Bs
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
list_to_ra_list(List, RAList) :-
|
|
% We reverse the list and count its elements using tail recursive code.
|
|
reverse_prepend_and_count(List, [], RevList, 0, Size),
|
|
( if Size = 0 then
|
|
RAList = nil
|
|
else
|
|
% Find the size of the biggest bintree in the RAList we need to
|
|
% construct.
|
|
get_max_bintree_size(Size, 1, MaxSkewBinary),
|
|
% Construct RAList back to front, by repeatedly "chomping off"
|
|
% a bintree's worth of items from RevList.
|
|
acc_ra_list(RevList, Size, MaxSkewBinary, nil, RAList)
|
|
).
|
|
|
|
% Reverse the given list (using non-native reverse) and count
|
|
% the number of items in the list at the same time.
|
|
%
|
|
:- pred reverse_prepend_and_count(list(T)::in, list(T)::in, list(T)::out,
|
|
int::in, int::out) is det.
|
|
|
|
reverse_prepend_and_count([], L, L, !Size).
|
|
reverse_prepend_and_count([H | T], L0, L, !Size) :-
|
|
!:Size = !.Size + 1,
|
|
reverse_prepend_and_count(T, [H | L0], L, !Size).
|
|
|
|
% Given CurSkewBinary, which is a skew-binary term that is guaranteed
|
|
% not to be bigger than Size, return the largest skew-binary term
|
|
% that is not bigger than Size.
|
|
%
|
|
% This will be the size of the last (and therefore largest) bintree
|
|
% in the ra_list we need to construct for a list with Size items.
|
|
%
|
|
:- pred get_max_bintree_size(int::in, int::in, int::out) is det.
|
|
|
|
get_max_bintree_size(Size, CurSkewBinary, MaxSkewBinary) :-
|
|
NextSkewBinary = (2 * CurSkewBinary) + 1,
|
|
( if Size >= NextSkewBinary then
|
|
get_max_bintree_size(Size, NextSkewBinary, MaxSkewBinary)
|
|
else
|
|
MaxSkewBinary = CurSkewBinary
|
|
).
|
|
|
|
% acc_ra_list(RevList0, SizeLeftover0, SkewBinary0, RAList0, RAList):
|
|
%
|
|
% We have processed some of the items at the end of the original list
|
|
% (and hence at the start of its reversed form) into RAList0. Convert
|
|
% the remaining elements of the reversed list, RevList0, into one or more
|
|
% cons cells to put in front of RAList0. SizeLeftover0 gives the number
|
|
% of items in RevList0, and SkewBinary0, which must be a skew-binary term,
|
|
% gives the size of the largest bintree in the cons cells we are to put
|
|
% in front of RAList0.
|
|
%
|
|
:- pred acc_ra_list(list(T)::in, int::in, int::in,
|
|
ra_list(T)::in, ra_list(T)::out) is det.
|
|
|
|
acc_ra_list(RevList0, SizeLeftover0, SkewBinary0, RAList0, RAList) :-
|
|
( if SizeLeftover0 > SkewBinary0 then
|
|
sized_prefix_to_bintree(SkewBinary0, BinTree0, RevList0, RevList1),
|
|
SizeLeftover1 = SizeLeftover0 - SkewBinary0,
|
|
SkewBinary1 = SkewBinary0 // 2,
|
|
RAList1 = cons(SkewBinary0, BinTree0, RAList0),
|
|
( if SizeLeftover1 < SkewBinary0 then
|
|
% This is the usual case when the cons cells in RevList1 and the
|
|
% next cons cell we put immediately in front of it contain
|
|
% bintrees of different sizes: the sum of all the (2^k) - 1
|
|
% over k in 1..(i-1) is strictly less than (2^i) - 1.
|
|
%
|
|
% For example:
|
|
%
|
|
% for i=2: 1 < 3
|
|
% for i=3: 1 + 3 < 7
|
|
% for i=4: 1 + 3 + 7 < 15
|
|
acc_ra_list(RevList1, SizeLeftover1, SkewBinary1, RAList1, RAList)
|
|
else if SizeLeftover1 = SkewBinary0 then
|
|
% This is how acc_ra_list terminates when the RAList we construct
|
|
% starts with two cons cells that contain bintrees of equal sizes.
|
|
sized_prefix_to_bintree(SkewBinary0, BinTree1, RevList1, RevList),
|
|
RAList = cons(SkewBinary0, BinTree1, RAList0),
|
|
expect(unify(RevList, []), $pred, "RevList != []")
|
|
else
|
|
unexpected($pred, "SizeLeftover0 > SkewBinary0")
|
|
)
|
|
else if SizeLeftover0 < SkewBinary0 then
|
|
SkewBinary1 = SkewBinary0 // 2,
|
|
acc_ra_list(RevList0, SizeLeftover0, SkewBinary1, RAList0, RAList)
|
|
else
|
|
% This is how acc_ra_list terminates when the RAList we construct
|
|
% starts with two cons cells that contain bintrees of non-equal sizes.
|
|
sized_prefix_to_bintree(SkewBinary0, BinTree, RevList0, RevList1),
|
|
SizeLeftover1 = SizeLeftover0 - SkewBinary0,
|
|
RAList = cons(SkewBinary0, BinTree, RAList0),
|
|
expect(unify(RevList1, []), $pred, "RevList1 != []"),
|
|
expect(unify(SizeLeftover1, 0), $pred, "SizeLeftover1 != 0")
|
|
).
|
|
|
|
% sized_prefix_to_bintree(BinTreeSize, BinTree, !RevList) :-
|
|
%
|
|
% Take the first BinTreeSize elements of !RevList, and construct
|
|
% a perfectly balanced binary tree out of them, while undoing the reversal.
|
|
%
|
|
% BinTreeSize must be a skew-binary term.
|
|
%
|
|
:- pred sized_prefix_to_bintree(int::in, ra_list_bintree(T)::out,
|
|
list(T)::in, list(T)::out) is det.
|
|
|
|
sized_prefix_to_bintree(BinTreeSize, BinTree, !RevList) :-
|
|
( if BinTreeSize = 1 then
|
|
get_next_item_from_rev_list(Head, !RevList),
|
|
BinTree = leaf(Head)
|
|
else
|
|
% Given BinTreeSize = (2^k) - 1, this sets BinTreeSize2
|
|
% to (2^(k-1)) - 1, i.e. to the next lower skew-binary term.
|
|
BinTreeSize2 = BinTreeSize // 2,
|
|
sized_prefix_to_bintree(BinTreeSize2, R, !RevList),
|
|
sized_prefix_to_bintree(BinTreeSize2, L, !RevList),
|
|
get_next_item_from_rev_list(Head, !RevList),
|
|
BinTree = node(Head, L, R)
|
|
).
|
|
|
|
:- pred get_next_item_from_rev_list(T::out, list(T)::in, list(T)::out) is det.
|
|
|
|
get_next_item_from_rev_list(Head, !RevList) :-
|
|
(
|
|
!.RevList = [Head | !:RevList]
|
|
;
|
|
!.RevList = [],
|
|
unexpected($pred, "!.RevList = []")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
ra_list_to_list(RAList, List) :-
|
|
(
|
|
RAList = nil,
|
|
List = []
|
|
;
|
|
RAList = cons(_Size, BinTree, RAListTail),
|
|
ra_list_to_list(RAListTail, Tail),
|
|
ra_list_to_list_acc_bintree(BinTree, Tail, List)
|
|
).
|
|
|
|
:- pred ra_list_to_list_acc_bintree(ra_list_bintree(T)::in,
|
|
list(T)::in, list(T)::out) is det.
|
|
|
|
ra_list_to_list_acc_bintree(BinTree, Tail0, List) :-
|
|
(
|
|
BinTree = leaf(X),
|
|
List = [X | Tail0]
|
|
;
|
|
BinTree = node(X, L, R),
|
|
ra_list_to_list_acc_bintree(R, Tail0, Tail1),
|
|
ra_list_to_list_acc_bintree(L, Tail1, Tail2),
|
|
List = [X | Tail2]
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
map(_F, nil) = nil.
|
|
map(F, cons(Size, BinTree0, RAList0)) = cons(Size, BinTree, RAList) :-
|
|
BinTree = map_bintree(F, BinTree0),
|
|
RAList = map(F, RAList0).
|
|
|
|
map(_P, nil, nil).
|
|
map(P, cons(Size, BinTree0, RAList0), cons(Size, BinTree, RAList)) :-
|
|
map_bintree(P, BinTree0, BinTree),
|
|
map(P, RAList0, RAList).
|
|
|
|
:- func map_bintree(func(T1) = T2, ra_list_bintree(T1)) = ra_list_bintree(T2).
|
|
:- pred map_bintree(pred(T1, T2), ra_list_bintree(T1), ra_list_bintree(T2)).
|
|
:- mode map_bintree(in(pred(in, out) is det), in, out) is det.
|
|
:- mode map_bintree(in(pred(in, out) is cc_multi), in, out) is cc_multi.
|
|
:- mode map_bintree(in(pred(in, out) is semidet), in, out) is semidet.
|
|
:- mode map_bintree(in(pred(in, out) is multi), in, out) is multi.
|
|
:- mode map_bintree(in(pred(in, out) is nondet), in, out) is nondet.
|
|
:- mode map_bintree(in(pred(in, in) is semidet), in, in) is semidet.
|
|
|
|
map_bintree(F, BinTree0) = BinTree :-
|
|
(
|
|
BinTree0 = leaf(X0),
|
|
X = F(X0),
|
|
BinTree = leaf(X)
|
|
;
|
|
BinTree0 = node(X0, L0, R0),
|
|
X = F(X0),
|
|
L = map_bintree(F, L0),
|
|
R = map_bintree(F, R0),
|
|
BinTree = node(X, L, R)
|
|
).
|
|
|
|
map_bintree(P, BinTree0, BinTree) :-
|
|
(
|
|
BinTree0 = leaf(X0),
|
|
P(X0, X),
|
|
BinTree = leaf(X)
|
|
;
|
|
BinTree0 = node(X0, L0, R0),
|
|
P(X0, X),
|
|
map_bintree(P, L0, L),
|
|
map_bintree(P, R0, R),
|
|
BinTree = node(X, L, R)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
foldl(_F, nil, !.A) = !.A.
|
|
foldl(F, cons(_Size, BinTree, RAList), !.A) = !:A :-
|
|
!:A = foldl_bintree(F, BinTree, !.A),
|
|
!:A = foldl(F, RAList, !.A).
|
|
|
|
foldl(_P, nil, !A).
|
|
foldl(P, cons(_Size, BinTree, RAList), !A) :-
|
|
foldl_bintree(P, BinTree, !A),
|
|
foldl(P, RAList, !A).
|
|
|
|
:- func foldl_bintree(func(T, A) = A, ra_list_bintree(T), A) = A.
|
|
:- pred foldl_bintree(pred(T, A, A), ra_list_bintree(T), A, A).
|
|
:- mode foldl_bintree(in(pred(in, in, out) is det),
|
|
in, in, out) is det.
|
|
:- mode foldl_bintree(in(pred(in, mdi, muo) is det),
|
|
in, mdi, muo) is det.
|
|
:- mode foldl_bintree(in(pred(in, di, uo) is det),
|
|
in, di, uo) is det.
|
|
:- mode foldl_bintree(in(pred(in, in, out) is semidet),
|
|
in, in, out) is semidet.
|
|
:- mode foldl_bintree(in(pred(in, mdi, muo) is semidet),
|
|
in, mdi, muo) is semidet.
|
|
:- mode foldl_bintree(in(pred(in, di, uo) is semidet),
|
|
in, di, uo) is semidet.
|
|
:- mode foldl_bintree(in(pred(in, in, out) is multi),
|
|
in, in, out) is multi.
|
|
:- mode foldl_bintree(in(pred(in, in, out) is nondet),
|
|
in, in, out) is nondet.
|
|
:- mode foldl_bintree(in(pred(in, mdi, muo) is nondet),
|
|
in, mdi, muo) is nondet.
|
|
:- mode foldl_bintree(in(pred(in, in, out) is cc_multi),
|
|
in, in, out) is cc_multi.
|
|
:- mode foldl_bintree(in(pred(in, di, uo) is cc_multi),
|
|
in, di, uo) is cc_multi.
|
|
|
|
foldl_bintree(F, BinTree, !.A) = !:A :-
|
|
(
|
|
BinTree = leaf(X),
|
|
!:A = F(X, !.A)
|
|
;
|
|
BinTree = node(X, L, R),
|
|
!:A = F(X, !.A),
|
|
!:A = foldl_bintree(F, L, !.A),
|
|
!:A = foldl_bintree(F, R, !.A)
|
|
).
|
|
|
|
foldl_bintree(P, BinTree, !A) :-
|
|
(
|
|
BinTree = leaf(X),
|
|
P(X, !A)
|
|
;
|
|
BinTree = node(X, L, R),
|
|
P(X, !A),
|
|
foldl_bintree(P, L, !A),
|
|
foldl_bintree(P, R, !A)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
foldr(_F, nil, !.A) = !.A.
|
|
foldr(F, cons(_Size, BinTree, RAList), !.A) = !:A :-
|
|
!:A = foldr(F, RAList, !.A),
|
|
!:A = foldr_bintree(F, BinTree, !.A).
|
|
|
|
foldr(_P, nil, !A).
|
|
foldr(P, cons(_Size, BinTree, RAList), !A) :-
|
|
foldr(P, RAList, !A),
|
|
foldr_bintree(P, BinTree, !A).
|
|
|
|
:- func foldr_bintree(func(T, A) = A, ra_list_bintree(T), A) = A.
|
|
:- pred foldr_bintree(pred(T, A, A), ra_list_bintree(T), A, A).
|
|
:- mode foldr_bintree(in(pred(in, in, out) is det),
|
|
in, in, out) is det.
|
|
:- mode foldr_bintree(in(pred(in, mdi, muo) is det),
|
|
in, mdi, muo) is det.
|
|
:- mode foldr_bintree(in(pred(in, di, uo) is det),
|
|
in, di, uo) is det.
|
|
:- mode foldr_bintree(in(pred(in, in, out) is semidet),
|
|
in, in, out) is semidet.
|
|
:- mode foldr_bintree(in(pred(in, mdi, muo) is semidet),
|
|
in, mdi, muo) is semidet.
|
|
:- mode foldr_bintree(in(pred(in, di, uo) is semidet),
|
|
in, di, uo) is semidet.
|
|
:- mode foldr_bintree(in(pred(in, in, out) is multi),
|
|
in, in, out) is multi.
|
|
:- mode foldr_bintree(in(pred(in, in, out) is nondet),
|
|
in, in, out) is nondet.
|
|
:- mode foldr_bintree(in(pred(in, mdi, muo) is nondet),
|
|
in, mdi, muo) is nondet.
|
|
:- mode foldr_bintree(in(pred(in, in, out) is cc_multi),
|
|
in, in, out) is cc_multi.
|
|
:- mode foldr_bintree(in(pred(in, di, uo) is cc_multi),
|
|
in, di, uo) is cc_multi.
|
|
|
|
foldr_bintree(F, BinTree, !.A) = !:A :-
|
|
(
|
|
BinTree = leaf(X),
|
|
!:A = F(X, !.A)
|
|
;
|
|
BinTree = node(X, L, R),
|
|
!:A = foldr_bintree(F, R, !.A),
|
|
!:A = foldr_bintree(F, L, !.A),
|
|
!:A = F(X, !.A)
|
|
).
|
|
|
|
foldr_bintree(P, BinTree, !A) :-
|
|
(
|
|
BinTree = leaf(X),
|
|
P(X, !A)
|
|
;
|
|
BinTree = node(X, L, R),
|
|
foldr_bintree(P, R, !A),
|
|
foldr_bintree(P, L, !A),
|
|
P(X, !A)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ra_list.
|
|
%---------------------------------------------------------------------------%
|