%--------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%--------------------------------------------------%
% Copyright (C) 2002-2011 The University of Melbourne.
% Copyright (C) 2013-2018 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%--------------------------------------------------%
%
% File: cord.m.
% Author: Ralph Becket <rafe@cs.mu.oz.au>
% Stability: medium.
%
% A cord is a sequence type supporting O(1) consing and concatenation.
% A cord is essentially a tree structure with data stored in the leaf nodes.
% Joining two cords together to construct a new cord is therefore an O(1)
% operation.
%
% This data type is intended for situations where efficient, linearised
% collection of data is required.
%
% While this data type presents a list-like interface, calls to list/1 and
% head_tail/3 in particular are O(n) in the size of the cord.
%
%--------------------------------------------------%
%--------------------------------------------------%
:- module cord.
:- interface.
:- import_module list.
%--------------------------------------------------%
% Cords that contain the same members in the same order will not
% necessarily have the same representation and will, therefore,
% not necessarily either unify or compare as equal.
%
% The exception to this rule is that the empty cord does have a
% unique representation.
%
:- type cord(T).
% Return the empty cord.
%
:- func init = cord(T).
% The unique representation for the empty cord:
%
% list(empty) = []
%
:- func empty = cord(T).
% Succeed iff the given cord is empty.
%
:- pred is_empty(cord(T)::in) is semidet.
% list(singleton(X)) = [X]
%
:- func singleton(T) = cord(T).
% list(from_list(Xs)) = Xs
% An O(1) operation.
%
:- func from_list(list(T)) = cord(T).
% The list of data in a cord:
%
% list(empty ) = []
% list(from_list(Xs)) = Xs
% list(cons(X, C) ) = [X | list(C)]
% list(TA ++ TB ) = list(TA) ++ list(TB)
%
:- func list(cord(T)) = list(T).
% A synonym for the list/1.
%
:- func to_list(cord(T)) = list(T).
% rev_list(Cord) = list.reverse(list(Cord).
%
:- func rev_list(cord(T)) = list(T).
% A synonym for rev_list/1.
%
:- func to_rev_list(cord(T)) = list(T).
% Cord = condense(CordOfCords):
%
% `Cord' is the result of concatenating all the elements of `CordOfCords'.
%
:- func condense(cord(cord(T))) = cord(T).
% list(cons(X, C)) = [X | list(C)]
% An O(1) operation.
%
:- func cons(T, cord(T)) = cord(T).
:- pred cons(T::in, cord(T)::in, cord(T)::out) is det.
% list(snoc(C, X)) = list(C) ++ [X]
% An O(1) operation.
%
:- func snoc(cord(T), T) = cord(T).
:- pred snoc(T::in, cord(T)::in, cord(T)::out) is det.
% list(CA ++ CB) = list(CA) ++ list(CB)
% An O(1) operation.
%
:- func cord(T) ++ cord(T) = cord(T).
% Append together a list of cords.
%
:- func cord_list_to_cord(list(cord(T))) = cord(T).
% Reverse the given list (of cords), and then append together
% the resulting list of cords.
%
:- func rev_cord_list_to_cord(list(cord(T))) = cord(T).
% Append together a list of cords, and return the result as a list.
%
:- func cord_list_to_list(list(cord(T))) = list(T).
% Reverse the given list (of cords), and then append together
% the resulting list of cords, and return it as a list.
%
:- func rev_cord_list_to_list(list(cord(T))) = list(T).
% head_tail(C0, X, C) => list(C0) = [X | list(C)]
% not head_tail(C0, _, _) => C0 = empty
% An O(n) operation, although traversing an entire cord with
% head_tail/3 gives O(1) amortized cost for each call.
%
:- pred head_tail(cord(T)::in, T::out, cord(T)::out) is semidet.
% split_last(C0, C, X) => list(C0) = C ++ [X].
% not split_last(C0, _, _) => C0 = empty
% An O(n) operation, although traversing an entire cord with
% split_last/3 gives O(1) amortized cost for each call.
%
:- pred split_last(cord(T)::in, cord(T)::out, T::out) is semidet.
% get_first(C0, X) => some [C]: list(C0) = [X] ++ C.
% not get_first(C0, _) => C0 = empty
%
:- pred get_first(cord(T)::in, T::out) is semidet.
% get_last(C0, X) => some [C]: list(C0) = C ++ [X].
% not get_last(C0, _) => C0 = empty
%
:- pred get_last(cord(T)::in, T::out) is semidet.
% length(C) = list.length(list(C))
% An O(n) operation.
%
:- func length(cord(T)) = int.
% member(X, C) <=> list.member(X, list(C)).
%
:- pred member(T::out, cord(T)::in) is nondet.
% list(map(F, C)) = list.map(F, list(C))
%
:- func map(func(T) = U, cord(T)) = cord(U).
:- pred map_pred(pred(T, U)::in(pred(in, out) is det),
cord(T)::in, cord(U)::out) is det.
% filter(Pred, Cord, TrueCord):
%
% Pred is a closure with one input argument.
% For each member X of Cord,
% - if Pred(X) is true, then X is included in TrueCord.
%
:- pred filter(pred(T)::in(pred(in) is semidet),
cord(T)::in, cord(T)::out) is det.
% filter(Pred, Cord, TrueCord, FalseCord):
%
% Pred is a closure with one input argument.
% For each member X of Cord,
% - if Pred(X) is true, then X is included in TrueCord.
% - if Pred(X) is false, then X is included in FalseCord.
%
:- pred filter(pred(T)::in(pred(in) is semidet),
cord(T)::in, cord(T)::out, cord(T)::out) is det.
% foldl(F, C, A) = list.foldl(F, list(C), A).
%
:- func foldl(func(T, A) = A, cord(T), A) = A.
% foldl_pred(P, C, !AccA)
%
% Equivalent to list.foldl(P, list(C), !AccA), but faster.
%
:- pred foldl_pred(pred(T, A, A), cord(T), A, A).
:- mode foldl_pred(in(pred(in, in, out) is det), in, in, out) is det.
:- mode foldl_pred(in(pred(in, mdi, muo) is det), in, mdi, muo) is det.
:- mode foldl_pred(in(pred(in, di, uo) is det), in, di, uo) is det.
:- mode foldl_pred(in(pred(in, in, out) is semidet), in, in, out) is semidet.
:- mode foldl_pred(in(pred(in, mdi, muo) is semidet), in, mdi, muo) is semidet.
:- mode foldl_pred(in(pred(in, di, uo) is semidet), in, di, uo) is semidet.
% foldl2(P, C, !AccA, !AccB)
%
% Equivalent to list.foldl2(P, list(C), !AccA, !AccB), but faster.
%
:- pred foldl2(pred(T, A, A, B, B), cord(T), A, A, B, B).
:- mode foldl2(in(pred(in, in, out, in, out) is det),
in, in, out, in, out) is det.
:- mode foldl2(in(pred(in, in, out, mdi, muo) is det),
in, in, out, mdi, muo) is det.
:- mode foldl2(in(pred(in, in, out, di, uo) is det),
in, in, out, di, uo) is det.
:- mode foldl2(in(pred(in, in, out, in, out) is semidet),
in, in, out, in, out) is semidet.
:- mode foldl2(in(pred(in, in, out, mdi, muo) is semidet),
in, in, out, mdi, muo) is semidet.
:- mode foldl2(in(pred(in, in, out, di, uo) is semidet),
in, in, out, di, uo) is semidet.
% foldl3(P, C, !AccA, !AccB, !AccC)
%
% Equivalent to list.foldl3(P, list(C), !AccA, !AccB, !AccC), but faster.
%
:- pred foldl3(pred(T, A, A, B, B, C, C), cord(T), A, A, B, B, C, C).
:- mode foldl3(in(pred(in, in, out, in, out, in, out) is det),
in, in, out, in, out, in, out) is det.
:- mode foldl3(in(pred(in, in, out, in, out, mdi, muo) is det),
in, in, out, in, out, mdi, muo) is det.
:- mode foldl3(in(pred(in, in, out, in, out, di, uo) is det),
in, in, out, in, out, di, uo) is det.
:- mode foldl3(in(pred(in, in, out, in, out, in, out) is semidet),
in, in, out, in, out, in, out) is semidet.
:- mode foldl3(in(pred(in, in, out, in, out, mdi, muo) is semidet),
in, in, out, in, out, mdi, muo) is semidet.
:- mode foldl3(in(pred(in, in, out, in, out, di, uo) is semidet),
in, in, out, in, out, di, uo) is semidet.
% foldr(F, C, A) = list.foldr(F, list(C), A).
%
:- func foldr(func(T, A) = A, cord(T), A) = A.
% foldr(F, C, !AccA)
%
% Equivalent to list.foldr(F, list(C), !AccA), but faster.
%
:- pred foldr_pred(pred(T, A, A), cord(T), A, A).
:- mode foldr_pred(in(pred(in, in, out) is det), in, in, out) is det.
:- mode foldr_pred(in(pred(in, mdi, muo) is det), in, mdi, muo) is det.
:- mode foldr_pred(in(pred(in, di, uo) is det), in, di, uo) is det.
:- mode foldr_pred(in(pred(in, in, out) is semidet), in, in, out) is semidet.
:- mode foldr_pred(in(pred(in, mdi, muo) is semidet), in, mdi, muo) is semidet.
:- mode foldr_pred(in(pred(in, di, uo) is semidet), in, di, uo) is semidet.
% map_foldl(P, CA, CB, !Acc):
%
% This predicate calls P on each element of the input cord, working
% left to right. Each call to P transforms an element of the input cord
% to the corresponding element of the output cord, and updates the
% accumulator.
%
:- pred map_foldl(pred(A, B, C, C), cord(A), cord(B), C, C).
:- mode map_foldl(in(pred(in, out, in, out) is det), in, out, in, out)
is det.
:- mode map_foldl(in(pred(in, out, mdi, muo) is det), in, out, mdi, muo)
is det.
:- mode map_foldl(in(pred(in, out, di, uo) is det), in, out, di, uo)
is det.
:- mode map_foldl(in(pred(in, out, in, out) is semidet), in, out, in, out)
is semidet.
:- mode map_foldl(in(pred(in, out, mdi, muo) is semidet), in, out, mdi, muo)
is semidet.
:- mode map_foldl(in(pred(in, out, di, uo) is semidet), in, out, di, uo)
is semidet.
% As above, but with two accumulators.
%
:- pred map_foldl2(pred(A, B, C, C, D, D)::
in(pred(in, out, in, out, in, out) is det),
cord(A)::in, cord(B)::out, C::in, C::out, D::in, D::out) is det.
% As above, but with three accumulators.
%
:- pred map_foldl3(pred(A, B, C, C, D, D, E, E)::
in(pred(in, out, in, out, in, out, in, out) is det),
cord(A)::in, cord(B)::out, C::in, C::out, D::in, D::out, E::in, E::out)
is det.
% find_first_match(Pred, List, FirstMatch) takes a closure with one
% input argument. It returns the first element X of the cord (if any)
% for which Pred(X) is true.
%
:- pred find_first_match(pred(X)::in(pred(in) is semidet),
cord(X)::in, X::out) is semidet.
% equal(CA, CB) <=> list(CA) = list(CB).
% An O(n) operation where n = length(CA) + length(CB).
%
% (Note: the current implementation works exactly this way.)
%
:- pred equal(cord(T)::in, cord(T)::in) is semidet.
%--------------------------------------------------%
%--------------------------------------------------%