%--------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%--------------------------------------------------%
% Copyright (C) 1997,1999-2000,2002-2003,2005-2006 The University of Melbourne.
% Copyright (C) 2014-2018 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%--------------------------------------------------%
%
% File: bt_array.m
% Main author: bromage.
% Stability: medium-low
%
% This file contains a set of predicates for generating and 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 array datatype instead. (`bt_array' is
% supposed to stand for either "binary tree array" or "backtrackable array".)
%
% Implementation obscurity: This implementation is biased 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 resize
% and shrink for more details.
%
%--------------------------------------------------%
%--------------------------------------------------%
:- module bt_array.
:- interface.
:- import_module list.
:- type bt_array(T).
%--------------------------------------------------%
% make_empty_array(Low, Array) is true iff Array is a
% bt_array of size zero starting at index Low.
%
:- pred make_empty_array(int::in, bt_array(T)::out) is det.
:- func make_empty_array(int) = bt_array(T).
% init(Low, High, Init, Array) is true iff Array is a
% bt_array with bounds from Low to High whose elements each equal Init.
%
:- pred init(int::in, int::in, T::in, bt_array(T)::out) is det.
:- func init(int, int, T) = bt_array(T).
%--------------------------------------------------%
% Returns the lower bound of the array.
%
:- pred min(bt_array(_T)::in, int::out) is det.
:- func min(bt_array(_T)) = int.
% Returns the upper bound of the array.
% Returns lower bound - 1 for an empty array.
%
:- pred max(bt_array(_T)::in, int::out) is det.
:- func max(bt_array(_T)) = int.
% Returns the length of the array,
% i.e. upper bound - lower bound + 1.
%
:- pred size(bt_array(_T)::in, int::out) is det.
:- func size(bt_array(_T)) = int.
% bounds(Array, Min, Max) returns the lower and upper bounds of a bt_array.
% The upper bound will be the lower bound - 1 for an empty array.
%
:- pred bounds(bt_array(_T)::in, int::out, int::out) is det.
% in_bounds checks whether an index is in the bounds
% of a bt_array.
%
:- pred in_bounds(bt_array(_T)::in, int::in) is semidet.
%--------------------------------------------------%
% lookup returns the Nth element of a bt_array.
% It is an error if the index is out of bounds.
%
:- pred lookup(bt_array(T)::in, int::in, T::out) is det.
:- func lookup(bt_array(T), int) = T.
% semidet_lookup is like lookup except that it fails if the index is out of
% bounds.
%
:- pred semidet_lookup(bt_array(T)::in, int::in, T::out) is semidet.
% set sets the nth element of a bt_array, and returns the resulting
% bt_array. It is an error if the index is out of bounds.
%
:- pred set(bt_array(T)::in, int::in, T::in, bt_array(T)::out) is det.
:- func set(bt_array(T), int, T) = bt_array(T).
% set sets the nth element of a bt_array, and returns the
% resulting bt_array (good opportunity for destructive update ;-).
% It fails if the index is out of bounds.
%
:- pred semidet_set(bt_array(T)::in, int::in, T::in, bt_array(T)::out)
is semidet.
% `resize(BtArray0, Lo, Hi, Item, BtArray)' is true if BtArray
% is a bt_array created by expanding or shrinking 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 resize(bt_array(T)::in, int::in, int::in, T::in,
bt_array(T)::out) is det.
:- func resize(bt_array(T), int, int, T) = bt_array(T).
% shrink(BtArray0, Lo, Hi, Item, BtArray) is true 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 shrink(bt_array(T)::in, int::in, int::in, bt_array(T)::out)
is det.
:- func shrink(bt_array(T), int, int) = bt_array(T).
% from_list(Low, List, BtArray) takes a list (of possibly zero
% length), and returns a bt_array containing % those elements in the same
% order that they occurred in the list. The lower bound of the new array
% is `Low'.
:- pred from_list(int::in, list(T)::in, bt_array(T)::out) is det.
:- func from_list(int, list(T)) = bt_array(T).
% to_list takes a bt_array and returns a list containing
% the elements of the bt_array in the same order that they occurred
% in the bt_array.
%
:- pred to_list(bt_array(T)::in, list(T)::out) is det.
:- func to_list(bt_array(T)) = list(T).
% fetch_items takes a bt_array and a lower and upper index,
% and places those items in the bt_array between these indices into a list.
% It is an error if either index is out of bounds.
%
:- pred fetch_items(bt_array(T)::in, int::in, int::in, list(T)::out)
is det.
:- func fetch_items(bt_array(T), int, int) = list(T).
% bsearch takes a bt_array, an element to be matched and a
% comparison predicate and returns the position of the first occurrence
% in the bt_array of an element which is equivalent to the given one
% in the ordering provided. Assumes the bt_array is sorted according
% to this ordering. Fails if the element is not present.
%
:- pred bsearch(bt_array(T)::in, T::in,
comparison_pred(T)::in(comparison_pred), int::out) is semidet.
% Field selection for arrays.
% Array ^ elem(Index) = lookup(Array, Index).
%
:- func elem(int, bt_array(T)) = T.
% Field update for arrays.
% (Array ^ elem(Index) := Value) = set(Array, Index, Value).
%
:- func 'elem :='(int, bt_array(T), T) = bt_array(T).
%--------------------------------------------------%