%---------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 1994-1995, 1997-1999, 2003-2006, 2011 The University of Melbourne. % Copyright (C) 2014-2016, 2018, 2022, 2025 The Mercury team. % This file is distributed under the terms specified in COPYING.LIB. %---------------------------------------------------------------------------% % % File: queue.m. % Main author: fjh. % Stability: high. % % This file contains a queue ADT. % A queue holds a sequence of values, and provides operations % to insert values at the end of the queue (put) and remove them from % the front of the queue (get). % % This implementation is in terms of a pair of lists. % The put and get operations are amortized constant-time. % %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- module queue. :- interface. :- import_module list. %---------------------------------------------------------------------------% :- type queue(T). % init(Queue) is true if-and-only-if Queue is an empty queue. % :- func init = queue(T). :- pred init(queue(T)::out) is det. % 'queue_equal(Q1, Q2)' is true if-and-only-if Q1 and Q2 contain the same % elements in the same order. % :- pred equal(queue(T)::in, queue(T)::in) is semidet. % is_empty(Queue) is true if-and-only-if Queue is an empty queue. % :- pred is_empty(queue(T)::in) is semidet. % is_full(Queue) is intended to be true if-and-only-if Queue is a queue % whose capacity is exhausted. This implementation allows arbitrary-sized % queues, so is_full always fails. % :- pred is_full(queue(T)::in) is semidet. % put(Elem, Queue0, Queue) is true if-and-only-if Queue is the queue % which results from appending Elem onto the end of Queue0. % :- func put(queue(T), T) = queue(T). :- pred put(T::in, queue(T)::in, queue(T)::out) is det. % put_list(Elems, Queue0, Queue) is true if-and-only-if Queue is the queue % which results from inserting the items in the list Elems into Queue0. % :- func put_list(queue(T), list(T)) = queue(T). :- pred put_list(list(T)::in, queue(T)::in, queue(T)::out) is det. % first(Queue, Elem) is true if-and-only-if Queue is a non-empty queue % whose first element is Elem. % :- pred first(queue(T)::in, T::out) is semidet. % get(Elem, Queue0, Queue) is true if-and-only-if Queue0 is a non-empty % queue whose first element is Elem, and Queue the queue which results % from removing that element from the front of Queue0. % :- pred get(T::out, queue(T)::in, queue(T)::out) is semidet. % length(Queue, Length) is true if-and-only-if Queue is a queue % containing Length elements. % :- func length(queue(T)) = int. :- pred length(queue(T)::in, int::out) is det. % list_to_queue(List, Queue) is true if-and-only-if Queue is a queue % containing the elements of List, with the first element of List at % the head of the queue. % :- func list_to_queue(list(T)) = queue(T). :- pred list_to_queue(list(T)::in, queue(T)::out) is det. % A synonym for list_to_queue/1. % :- func from_list(list(T)) = queue(T). % to_list(Queue) = List is the inverse of from_list/1. % :- func to_list(queue(T)) = list(T). % delete_all(Elem, Queue0, Queue) is true if-and-only-if Queue is the same % queue as Queue0 with all occurrences of Elem removed from it. % :- func delete_all(queue(T), T) = queue(T). :- pred delete_all(T::in, queue(T)::in, queue(T)::out) is det. % put_on_front(Queue0, Elem) = Queue pushes Elem on to % the front of Queue0, giving Queue. % :- func put_on_front(queue(T), T) = queue(T). :- pred put_on_front(T::in, queue(T)::in, queue(T)::out) is det. % put_list_on_front(Queue0, Elems) = Queue pushes Elems % on to the front of Queue0, giving Queue (the N'th member % of Elems becomes the N'th member from the front of Queue). % :- func put_list_on_front(queue(T), list(T)) = queue(T). :- pred put_list_on_front(list(T)::in, queue(T)::in, queue(T)::out) is det. % get_from_back(Elem, Queue0, Queue) removes Elem from % the back of Queue0, giving Queue. % :- pred get_from_back(T::out, queue(T)::in, queue(T)::out) is semidet. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. :- import_module int. %---------------------------------------------------------------------------% % This implementation is in terms of a pair of lists: the list of items % in the queue is given by off_list ++ reverse(on_list). The reason for % the names is that we generally get items off the off_list and put them % on the on_list. We impose the extra constraint that the off_list field % is empty if and only if the queue as a whole is empty. % :- type queue(T) ---> queue( on_list :: list(T), off_list :: list(T) ). %---------------------------------------------------------------------------% init = Q :- queue.init(Q). init(queue([], [])). equal(queue(OnA, OffA), queue(OnB, OffB)) :- QA = OffA ++ list.reverse(OnA), QB = OffB ++ list.reverse(OnB), QA = QB. is_empty(queue(_, [])). is_full(_) :- semidet_fail. put(!.Q, T) = !:Q :- queue.put(T, !Q). put(Elem, queue(On0, Off0), queue(On, Off)) :- ( Off0 = [], On = On0, Off = [Elem] ; Off0 = [_ | _], On = [Elem | On0], Off = Off0 ). put_list(!.Q, Xs) = !:Q :- queue.put_list(Xs, !Q). put_list(Xs, queue(On0, Off0), queue(On, Off)) :- ( Off0 = [], On = On0, Off = Xs ; Off0 = [_ | _], Off = Off0, queue.put_list_2(Xs, On0, On) ). :- pred put_list_2(list(T)::in, list(T)::in, list(T)::out) is det. put_list_2([], On, On). put_list_2([X | Xs], On0, On) :- queue.put_list_2(Xs, [X | On0], On). first(queue(_, [Elem | _]), Elem). get(Elem, queue(On0, [Elem | Off0]), queue(On, Off)) :- ( Off0 = [], list.reverse(On0, Off), On = [] ; Off0 = [_ | _], On = On0, Off = Off0 ). length(Q) = N :- queue.length(Q, N). length(queue(On, Off), Length) :- list.length(On, LengthOn), list.length(Off, LengthOff), Length = LengthOn + LengthOff. list_to_queue(Xs) = Q :- queue.list_to_queue(Xs, Q). list_to_queue(List, queue([], List)). from_list(List) = queue([], List). to_list(queue(On, Off)) = Off ++ list.reverse(On). delete_all(!.Q, T) = !:Q :- queue.delete_all(T, !Q). delete_all(Elem ,queue(On0, Off0), queue(On, Off)) :- list.delete_all(On0, Elem, On1), list.delete_all(Off0, Elem, Off1), ( Off1 = [], list.reverse(On1, Off), On = [] ; Off1 = [_ | _], On = On1, Off = Off1 ). put_on_front(!.Queue, Elem) = !:Queue :- queue.put_on_front(Elem, !Queue). put_on_front(Elem, queue(On, Off), queue(On, [Elem | Off])). put_list_on_front(!.Queue, Elems) = !:Queue :- queue.put_list_on_front(Elems, !Queue). put_list_on_front(Elems, queue(On, Off), queue(On, Elems ++ Off)). get_from_back(Elem, queue(On0, Off0), queue(On, Off)) :- ( % The On list is non-empty and the last element in the queue % is the head of the On list. On0 = [Elem | On], Off = Off0 ; % The On list is empty. On0 = [], ( % The Off list contains a single element. Off0 = [Elem], On = [], Off = [] ; % The Off list contains two or more elements. We split it in two % and take the head of the new On list as Elem. Off0 = [_, _ | _], N = list.length(Off0), list.split_list(N / 2, Off0, Off, RevOn), [Elem | On] = list.reverse(RevOn) ) ). %---------------------------------------------------------------------------% :- end_module queue. %---------------------------------------------------------------------------%