summaryrefslogtreecommitdiff
path: root/src/priority_queue.erl
blob: 370d2e6614d4ac21a975a535f4006430fbc47da0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
%%   The contents of this file are subject to the Mozilla Public License
%%   Version 1.1 (the "License"); you may not use this file except in
%%   compliance with the License. You may obtain a copy of the License at
%%   http://www.mozilla.org/MPL/
%%
%%   Software distributed under the License is distributed on an "AS IS"
%%   basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
%%   License for the specific language governing rights and limitations
%%   under the License.
%%
%%   The Original Code is RabbitMQ.
%%
%%   The Initial Developers of the Original Code are LShift Ltd,
%%   Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd.
%%
%%   Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd,
%%   Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd
%%   are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial
%%   Technologies LLC, and Rabbit Technologies Ltd.
%%
%%   Portions created by LShift Ltd are Copyright (C) 2007-2009 LShift
%%   Ltd. Portions created by Cohesive Financial Technologies LLC are
%%   Copyright (C) 2007-2009 Cohesive Financial Technologies
%%   LLC. Portions created by Rabbit Technologies Ltd are Copyright
%%   (C) 2007-2009 Rabbit Technologies Ltd.
%%
%%   All Rights Reserved.
%%
%%   Contributor(s): ______________________________________.
%%

%% Priority queues have essentially the same interface as ordinary
%% queues, except that a) there is an in/3 that takes a priority, and
%% b) we have only implemented the core API we need.
%%
%% Priorities should be integers - the higher the value the higher the
%% priority - but we don't actually check that.
%%
%% in/2 inserts items with priority 0.
%%
%% We optimise the case where a priority queue is being used just like
%% an ordinary queue. When that is the case we represent the priority
%% queue as an ordinary queue. We could just call into the 'queue'
%% module for that, but for efficiency we implement the relevant
%% functions directly in here, thus saving on inter-module calls and
%% eliminating a level of boxing.
%%
%% When in/3 is invoked for the first time we change the
%% representation from an ordinary queue to a gb_tree with {Priority,
%% Counter} as the key. Counter is incremented with for every 'in',

-module(priority_queue).

-export([new/0, is_queue/1, is_empty/1, len/1, to_list/1, in/2, in/3, out/1]).

new() ->
    {queue, [], []}.

is_queue({queue, R, F}) when is_list(R), is_list(F) ->
    true;
is_queue({pqueue, Counter, _Tree}) when is_integer(Counter), Counter >= 0 ->
    true;
is_queue(_) ->
    false.

is_empty({queue, [], []}) ->
    true;
is_empty({queue, In,Out}) when is_list(In), is_list(Out) ->
    false;
is_empty({pqueue, _, Tree}) ->
    gb_trees:is_empty(Tree).

len({queue, R, F}) when is_list(R), is_list(F) ->
    length(R) + length(F);
len({pqueue, _, Tree}) ->
    gb_trees:size(Tree).

to_list({queue, In, Out}) when is_list(In), is_list(Out) ->
    [{0, V} || V <- Out ++ lists:reverse(In, [])];
to_list({pqueue, _, Tree}) ->
    [{-P, V} || {{P, _C}, V} <- gb_trees:to_list(Tree)].

in(X, {queue, [_] = In, []}) ->
    {queue, [X], In};
in(X, {queue, In, Out}) when is_list(In), is_list(Out) ->
    {queue, [X|In], Out};
in(Item, Other) ->
    in(Item, 0, Other).

in(Item, Priority, {queue, In, Out}) ->
    {Counter, Tree} = to_tree(In, Out),
    in(Item, Priority, {pqueue, Counter, Tree});
in(Item, Priority, {pqueue, Counter, Tree}) ->
    {pqueue, Counter + 1, gb_trees:insert({-Priority, Counter}, Item, Tree)}.

out({queue, [], []} = Q) ->
    {empty, Q};
out({queue, [V], []}) ->
    {{value, V}, {queue, [], []}};
out({queue, [Y|In], []}) ->
    [V|Out] = lists:reverse(In, []),
    {{value, V}, {queue, [Y], Out}};
out({queue, In, [V]}) when is_list(In) ->
    {{value,V}, r2f(In)};
out({queue, In,[V|Out]}) when is_list(In) ->
    {{value, V}, {queue, In, Out}};
out({pqueue, Counter, Tree}) ->
    {_, Item, Tree1} = gb_trees:take_smallest(Tree),
    {{value, Item}, case gb_trees:is_empty(Tree1) of
                        true  -> {queue, [], []};
                        false -> {pqueue, Counter, Tree1}
                    end}.

to_tree(In, Out) ->
    lists:foldl(fun (V, {C, T}) ->
                        {C + 1, gb_trees:insert({0, C}, V, T)}
                end, {0, gb_trees:empty()}, Out ++ lists:reverse(In, [])).

r2f([]) ->
    {queue, [], []};
r2f([_] = R) ->
    {queue, [], R};
r2f([X,Y]) ->
    {queue, [X], [Y]};
r2f([X,Y|R]) ->
    {queue, [X,Y], lists:reverse(R, [])}.