athos@331
|
1 |
#ifndef HUGO_PREFLOW_PUSH_HH
|
athos@331
|
2 |
#define HUGO_PREFLOW_PUSH_HH
|
athos@36
|
3 |
|
athos@331
|
4 |
//#include <algorithm>
|
athos@36
|
5 |
#include <list>
|
athos@36
|
6 |
#include <vector>
|
athos@331
|
7 |
#include <queue>
|
athos@36
|
8 |
//#include "pf_hiba.hh"
|
athos@36
|
9 |
//#include <marci_list_graph.hh>
|
athos@77
|
10 |
//#include <marci_graph_traits.hh>
|
athos@331
|
11 |
#include <invalid.h>
|
athos@331
|
12 |
#include <graph_wrapper.h>
|
athos@331
|
13 |
//#include <reverse_bfs.hh>
|
athos@36
|
14 |
|
athos@36
|
15 |
using namespace std;
|
athos@36
|
16 |
|
alpar@105
|
17 |
namespace hugo {
|
athos@36
|
18 |
|
athos@331
|
19 |
template <typename Graph, typename T>
|
athos@36
|
20 |
class preflow_push {
|
athos@36
|
21 |
|
athos@331
|
22 |
//Useful typedefs
|
athos@331
|
23 |
typedef typename Graph::Node Node;
|
athos@331
|
24 |
typedef typename Graph::NodeIt NodeIt;
|
athos@331
|
25 |
typedef typename Graph::Edge Edge;
|
athos@331
|
26 |
typedef typename Graph::OutEdgeIt OutEdgeIt;
|
athos@331
|
27 |
typedef typename Graph::InEdgeIt InEdgeIt;
|
athos@77
|
28 |
|
athos@77
|
29 |
|
athos@36
|
30 |
//---------------------------------------------
|
athos@36
|
31 |
//Parameters of the algorithm
|
athos@36
|
32 |
//---------------------------------------------
|
athos@36
|
33 |
//Fully examine an active node until excess becomes 0
|
athos@36
|
34 |
enum node_examination_t {examine_full, examine_to_relabel};
|
athos@36
|
35 |
//No more implemented yet:, examine_only_one_edge};
|
athos@36
|
36 |
node_examination_t node_examination;
|
athos@36
|
37 |
//Which implementation to be used
|
athos@36
|
38 |
enum implementation_t {impl_fifo, impl_highest_label};
|
athos@36
|
39 |
//No more implemented yet:};
|
athos@36
|
40 |
implementation_t implementation;
|
athos@36
|
41 |
//---------------------------------------------
|
athos@36
|
42 |
//Parameters of the algorithm
|
athos@36
|
43 |
//---------------------------------------------
|
athos@36
|
44 |
|
athos@36
|
45 |
private:
|
athos@36
|
46 |
//input
|
athos@331
|
47 |
Graph& G;
|
athos@331
|
48 |
Node s;
|
athos@331
|
49 |
Node t;
|
athos@331
|
50 |
typename Graph::EdgeMap<T> &capacity;
|
athos@331
|
51 |
|
athos@36
|
52 |
//output
|
athos@331
|
53 |
typename Graph::EdgeMap<T> preflow;
|
athos@36
|
54 |
T maxflow_value;
|
athos@36
|
55 |
|
athos@36
|
56 |
//auxiliary variables for computation
|
athos@331
|
57 |
//The number of the nodes
|
athos@36
|
58 |
int number_of_nodes;
|
athos@331
|
59 |
//A nodemap for the level
|
athos@331
|
60 |
typename Graph::NodeMap<int> level;
|
athos@331
|
61 |
//A nodemap for the excess
|
athos@331
|
62 |
typename Graph::NodeMap<T> excess;
|
athos@36
|
63 |
|
athos@36
|
64 |
//Number of nodes on each level
|
athos@36
|
65 |
vector<int> num_of_nodes_on_level;
|
athos@36
|
66 |
|
athos@36
|
67 |
//For the FIFO implementation
|
athos@331
|
68 |
list<Node> fifo_nodes;
|
athos@36
|
69 |
//For 'highest label' implementation
|
athos@36
|
70 |
int highest_active;
|
athos@36
|
71 |
//int second_highest_active;
|
athos@331
|
72 |
vector< list<Node> > active_nodes;
|
athos@36
|
73 |
|
athos@36
|
74 |
public:
|
athos@36
|
75 |
|
athos@36
|
76 |
//Constructing the object using the graph, source, sink and capacity vector
|
athos@36
|
77 |
preflow_push(
|
athos@331
|
78 |
Graph& _G,
|
athos@331
|
79 |
Node _s,
|
athos@331
|
80 |
Node _t,
|
athos@331
|
81 |
typename Graph::EdgeMap<T> & _capacity)
|
athos@36
|
82 |
: G(_G), s(_s), t(_t),
|
athos@36
|
83 |
capacity(_capacity),
|
athos@36
|
84 |
preflow(_G),
|
athos@36
|
85 |
//Counting the number of nodes
|
athos@77
|
86 |
//number_of_nodes(count(G.first<EachNodeIt>())),
|
athos@77
|
87 |
number_of_nodes(G.nodeNum()),
|
athos@77
|
88 |
|
athos@36
|
89 |
level(_G),
|
athos@36
|
90 |
excess(_G)//,
|
athos@36
|
91 |
// Default constructor: active_nodes()
|
athos@36
|
92 |
{
|
athos@36
|
93 |
//Simplest parameter settings
|
athos@36
|
94 |
node_examination = examine_full;//examine_to_relabel;//
|
athos@36
|
95 |
//Which implementation to be usedexamine_full
|
athos@36
|
96 |
implementation = impl_highest_label;//impl_fifo;
|
athos@36
|
97 |
|
athos@36
|
98 |
//
|
athos@36
|
99 |
num_of_nodes_on_level.resize(2*number_of_nodes-1);
|
athos@36
|
100 |
num_of_nodes_on_level.clear();
|
athos@36
|
101 |
|
athos@36
|
102 |
switch(implementation){
|
athos@36
|
103 |
case impl_highest_label :{
|
athos@331
|
104 |
active_nodes.clear();
|
athos@36
|
105 |
active_nodes.resize(2*number_of_nodes-1);
|
athos@331
|
106 |
|
athos@36
|
107 |
break;
|
athos@36
|
108 |
}
|
athos@36
|
109 |
default:
|
athos@36
|
110 |
break;
|
athos@36
|
111 |
}
|
athos@36
|
112 |
|
athos@36
|
113 |
}
|
athos@36
|
114 |
|
athos@36
|
115 |
//Returns the value of a maximal flow
|
athos@36
|
116 |
T run();
|
athos@36
|
117 |
|
athos@331
|
118 |
typename Graph::EdgeMap<T> getmaxflow(){
|
athos@36
|
119 |
return preflow;
|
athos@36
|
120 |
}
|
athos@36
|
121 |
|
athos@36
|
122 |
|
athos@36
|
123 |
private:
|
athos@36
|
124 |
//For testing purposes only
|
athos@36
|
125 |
//Lists the node_properties
|
athos@331
|
126 |
void write_property_vector(typename Graph::NodeMap<T> a,
|
athos@331
|
127 |
//node_property_vector<Graph, T> a,
|
athos@36
|
128 |
char* prop_name="property"){
|
athos@331
|
129 |
for(NodeIt i=G.template first<NodeIt>(); G.valid(i); G.next(i)) {
|
athos@331
|
130 |
cout<<"Node id.: "<<G.id(i)<<", "<<prop_name<<" value: "<<a[i]<<endl;
|
athos@36
|
131 |
}
|
athos@36
|
132 |
cout<<endl;
|
athos@36
|
133 |
}
|
athos@36
|
134 |
|
athos@36
|
135 |
//Modifies the excess of the node and makes sufficient changes
|
athos@331
|
136 |
void modify_excess(const Node& a ,T v){
|
athos@331
|
137 |
//T old_value=excess[a];
|
athos@331
|
138 |
excess[a] += v;
|
athos@36
|
139 |
}
|
athos@36
|
140 |
|
athos@36
|
141 |
//This private procedure is supposed to modify the preflow on edge j
|
athos@36
|
142 |
//by value v (which can be positive or negative as well)
|
athos@36
|
143 |
//and maintain the excess on the head and tail
|
athos@36
|
144 |
//Here we do not check whether this is possible or not
|
athos@331
|
145 |
void modify_preflow(Edge j, const T& v){
|
athos@36
|
146 |
|
athos@36
|
147 |
//Modifiyng the edge
|
athos@331
|
148 |
preflow[j] += v;
|
athos@36
|
149 |
|
athos@36
|
150 |
|
athos@36
|
151 |
//Modifiyng the head
|
athos@36
|
152 |
modify_excess(G.head(j),v);
|
athos@36
|
153 |
|
athos@36
|
154 |
//Modifiyng the tail
|
athos@36
|
155 |
modify_excess(G.tail(j),-v);
|
athos@36
|
156 |
|
athos@36
|
157 |
}
|
athos@36
|
158 |
|
athos@36
|
159 |
//Gives the active node to work with
|
athos@36
|
160 |
//(depending on the implementation to be used)
|
athos@331
|
161 |
Node get_active_node(){
|
athos@119
|
162 |
|
athos@36
|
163 |
|
athos@36
|
164 |
switch(implementation) {
|
athos@36
|
165 |
case impl_highest_label : {
|
athos@36
|
166 |
|
athos@331
|
167 |
//First need to find the highest label for which there's an active node
|
athos@36
|
168 |
while( highest_active>=0 && active_nodes[highest_active].empty() ){
|
athos@36
|
169 |
--highest_active;
|
athos@36
|
170 |
}
|
athos@36
|
171 |
|
athos@36
|
172 |
if( highest_active>=0) {
|
athos@119
|
173 |
|
athos@119
|
174 |
|
athos@331
|
175 |
Node a=active_nodes[highest_active].front();
|
athos@36
|
176 |
active_nodes[highest_active].pop_front();
|
athos@119
|
177 |
|
athos@36
|
178 |
return a;
|
athos@36
|
179 |
}
|
athos@36
|
180 |
else {
|
athos@331
|
181 |
return INVALID;
|
athos@36
|
182 |
}
|
athos@36
|
183 |
|
athos@36
|
184 |
break;
|
athos@36
|
185 |
|
athos@36
|
186 |
}
|
athos@36
|
187 |
case impl_fifo : {
|
athos@36
|
188 |
|
athos@36
|
189 |
if( ! fifo_nodes.empty() ) {
|
athos@331
|
190 |
Node a=fifo_nodes.front();
|
athos@36
|
191 |
fifo_nodes.pop_front();
|
athos@36
|
192 |
return a;
|
athos@36
|
193 |
}
|
athos@36
|
194 |
else {
|
athos@331
|
195 |
return INVALID;
|
athos@36
|
196 |
}
|
athos@36
|
197 |
break;
|
athos@36
|
198 |
}
|
athos@36
|
199 |
}
|
athos@36
|
200 |
//
|
athos@331
|
201 |
return INVALID;
|
athos@36
|
202 |
}
|
athos@36
|
203 |
|
athos@36
|
204 |
//Puts node 'a' among the active nodes
|
athos@331
|
205 |
void make_active(const Node& a){
|
athos@36
|
206 |
//s and t never become active
|
athos@36
|
207 |
if (a!=s && a!= t){
|
athos@36
|
208 |
switch(implementation){
|
athos@36
|
209 |
case impl_highest_label :
|
athos@331
|
210 |
active_nodes[level[a]].push_back(a);
|
athos@36
|
211 |
break;
|
athos@36
|
212 |
case impl_fifo :
|
athos@36
|
213 |
fifo_nodes.push_back(a);
|
athos@36
|
214 |
break;
|
athos@36
|
215 |
}
|
athos@36
|
216 |
|
athos@36
|
217 |
}
|
athos@36
|
218 |
|
athos@36
|
219 |
//Update highest_active label
|
athos@331
|
220 |
if (highest_active<level[a]){
|
athos@331
|
221 |
highest_active=level[a];
|
athos@36
|
222 |
}
|
athos@36
|
223 |
|
athos@36
|
224 |
}
|
athos@36
|
225 |
|
athos@36
|
226 |
//Changes the level of node a and make sufficent changes
|
athos@331
|
227 |
void change_level_to(Node a, int new_value){
|
athos@331
|
228 |
int seged = level[a];
|
athos@77
|
229 |
level.set(a,new_value);
|
athos@36
|
230 |
--num_of_nodes_on_level[seged];
|
athos@36
|
231 |
++num_of_nodes_on_level[new_value];
|
athos@36
|
232 |
}
|
athos@36
|
233 |
|
athos@36
|
234 |
//Collection of things useful (or necessary) to do before running
|
athos@77
|
235 |
|
athos@36
|
236 |
void preprocess(){
|
athos@36
|
237 |
|
athos@36
|
238 |
//---------------------------------------
|
athos@36
|
239 |
//Initialize parameters
|
athos@36
|
240 |
//---------------------------------------
|
athos@36
|
241 |
|
athos@36
|
242 |
//Setting starting preflow, level and excess values to zero
|
athos@36
|
243 |
//This can be important, if the algorithm is run more then once
|
athos@331
|
244 |
for(NodeIt i=G.template first<NodeIt>(); G.valid(i); G.next(i)) {
|
athos@77
|
245 |
level.set(i,0);
|
athos@77
|
246 |
excess.set(i,0);
|
athos@331
|
247 |
for(OutEdgeIt j=G.template first<OutEdgeIt>(i); G.valid(j); G.next(j))
|
athos@77
|
248 |
preflow.set(j, 0);
|
athos@36
|
249 |
}
|
athos@36
|
250 |
num_of_nodes_on_level[0]=number_of_nodes;
|
athos@36
|
251 |
highest_active=0;
|
athos@36
|
252 |
//---------------------------------------
|
athos@36
|
253 |
//Initialize parameters
|
athos@36
|
254 |
//---------------------------------------
|
athos@36
|
255 |
|
athos@36
|
256 |
|
athos@36
|
257 |
//------------------------------------
|
athos@36
|
258 |
//This is the only part that uses BFS
|
athos@36
|
259 |
//------------------------------------
|
athos@331
|
260 |
|
athos@331
|
261 |
/*Reverse_bfs from t, to find the starting level.*/
|
athos@331
|
262 |
//Copyright: Jacint
|
athos@331
|
263 |
change_level_to(t,0);
|
athos@331
|
264 |
|
athos@331
|
265 |
std::queue<Node> bfs_queue;
|
athos@331
|
266 |
bfs_queue.push(t);
|
athos@331
|
267 |
|
athos@331
|
268 |
while (!bfs_queue.empty()) {
|
athos@331
|
269 |
|
athos@331
|
270 |
Node v=bfs_queue.front();
|
athos@331
|
271 |
bfs_queue.pop();
|
athos@331
|
272 |
int l=level[v]+1;
|
athos@331
|
273 |
|
athos@331
|
274 |
InEdgeIt e;
|
athos@331
|
275 |
for(G.first(e,v); G.valid(e); G.next(e)) {
|
athos@331
|
276 |
Node w=G.tail(e);
|
athos@331
|
277 |
if ( level[w] == number_of_nodes && w != s ) {
|
athos@331
|
278 |
bfs_queue.push(w);
|
athos@331
|
279 |
//Node first=level_list[l];
|
athos@331
|
280 |
//if ( G.valid(first) ) left.set(first,w);
|
athos@331
|
281 |
//right.set(w,first);
|
athos@331
|
282 |
//level_list[l]=w;
|
athos@331
|
283 |
change_level_to(w, l);
|
athos@331
|
284 |
//level.set(w, l);
|
athos@331
|
285 |
}
|
athos@331
|
286 |
}
|
athos@331
|
287 |
}
|
athos@331
|
288 |
change_level_to(s,number_of_nodes);
|
athos@331
|
289 |
//level.set(s,number_of_nodes);
|
athos@331
|
290 |
|
athos@331
|
291 |
/*
|
athos@36
|
292 |
//Setting starting level values using reverse bfs
|
athos@331
|
293 |
reverse_bfs<Graph> rev_bfs(G,t);
|
athos@36
|
294 |
rev_bfs.run();
|
athos@36
|
295 |
//write_property_vector(rev_bfs.dist,"rev_bfs");
|
athos@331
|
296 |
for(NodeIt i=G.template first<NodeIt>(); G.valid(i); G.next(i)) {
|
athos@36
|
297 |
change_level_to(i,rev_bfs.dist(i));
|
athos@36
|
298 |
//level.put(i,rev_bfs.dist.get(i));
|
athos@36
|
299 |
}
|
athos@331
|
300 |
*/
|
athos@36
|
301 |
//------------------------------------
|
athos@36
|
302 |
//This is the only part that uses BFS
|
athos@36
|
303 |
//------------------------------------
|
athos@36
|
304 |
|
athos@36
|
305 |
|
athos@36
|
306 |
//Starting level of s
|
athos@36
|
307 |
change_level_to(s,number_of_nodes);
|
athos@36
|
308 |
//level.put(s,number_of_nodes);
|
athos@36
|
309 |
|
athos@36
|
310 |
|
athos@36
|
311 |
//we push as much preflow from s as possible to start with
|
athos@331
|
312 |
for(OutEdgeIt j=G.template first<OutEdgeIt>(s); G.valid(j); G.next(j)){
|
athos@331
|
313 |
modify_preflow(j,capacity[j] );
|
athos@36
|
314 |
make_active(G.head(j));
|
athos@331
|
315 |
int lev=level[G.head(j)];
|
athos@36
|
316 |
if(highest_active<lev){
|
athos@36
|
317 |
highest_active=lev;
|
athos@36
|
318 |
}
|
athos@36
|
319 |
}
|
athos@36
|
320 |
//cout<<highest_active<<endl;
|
athos@36
|
321 |
}
|
athos@36
|
322 |
|
athos@36
|
323 |
|
athos@36
|
324 |
//If the preflow is less than the capacity on the given edge
|
athos@36
|
325 |
//then it is an edge in the residual graph
|
athos@331
|
326 |
bool is_admissible_forward_edge(Edge j, int& new_level){
|
athos@119
|
327 |
|
athos@331
|
328 |
if (capacity[j]>preflow[j]){
|
athos@331
|
329 |
if(level[G.tail(j)]==level[G.head(j)]+1){
|
athos@36
|
330 |
return true;
|
athos@36
|
331 |
}
|
athos@36
|
332 |
else{
|
athos@331
|
333 |
if (level[G.head(j)] < new_level)
|
athos@331
|
334 |
new_level=level[G.head(j)];
|
athos@36
|
335 |
}
|
athos@36
|
336 |
}
|
athos@36
|
337 |
return false;
|
athos@36
|
338 |
}
|
athos@36
|
339 |
|
athos@36
|
340 |
//If the preflow is greater than 0 on the given edge
|
athos@36
|
341 |
//then the edge reversd is an edge in the residual graph
|
athos@331
|
342 |
bool is_admissible_backward_edge(Edge j, int& new_level){
|
athos@119
|
343 |
|
athos@331
|
344 |
if (0<preflow[j]){
|
athos@331
|
345 |
if(level[G.tail(j)]==level[G.head(j)]-1){
|
athos@119
|
346 |
|
athos@36
|
347 |
return true;
|
athos@36
|
348 |
}
|
athos@36
|
349 |
else{
|
athos@331
|
350 |
if (level[G.tail(j)] < new_level)
|
athos@331
|
351 |
new_level=level[G.tail(j)];
|
athos@36
|
352 |
}
|
athos@36
|
353 |
|
athos@36
|
354 |
}
|
athos@36
|
355 |
return false;
|
athos@36
|
356 |
}
|
athos@36
|
357 |
|
athos@36
|
358 |
|
athos@36
|
359 |
}; //class preflow_push
|
athos@36
|
360 |
|
athos@331
|
361 |
template<typename Graph, typename T>
|
athos@331
|
362 |
T preflow_push<Graph, T>::run() {
|
athos@331
|
363 |
|
athos@331
|
364 |
//We need a residual graph
|
athos@331
|
365 |
ResGraphType res_graph(G, preflow, capacity);
|
athos@36
|
366 |
|
athos@36
|
367 |
preprocess();
|
athos@119
|
368 |
//write_property_vector(level,"level");
|
athos@36
|
369 |
T e,v;
|
athos@331
|
370 |
Node a;
|
athos@331
|
371 |
while (a=get_active_node(), G.valid(a)){
|
athos@119
|
372 |
|
athos@331
|
373 |
bool go_to_next_node=false;
|
athos@331
|
374 |
e = excess[a];
|
athos@331
|
375 |
while (!go_to_next_node){
|
athos@36
|
376 |
|
athos@77
|
377 |
//Initial value for the new level for the active node we are dealing with
|
athos@77
|
378 |
int new_level=2*number_of_nodes;
|
athos@331
|
379 |
|
athos@331
|
380 |
|
athos@36
|
381 |
//Out edges from node a
|
athos@36
|
382 |
{
|
athos@77
|
383 |
OutEdgeIt j=G.template first<OutEdgeIt>(a);
|
athos@331
|
384 |
while (G.valid(j) && e){
|
athos@36
|
385 |
|
athos@36
|
386 |
if (is_admissible_forward_edge(j,new_level)){
|
athos@331
|
387 |
v=min(e,capacity[j] - preflow[j]);
|
athos@36
|
388 |
e -= v;
|
athos@36
|
389 |
//New node might become active
|
athos@331
|
390 |
if (excess[G.head(j)]==0){
|
athos@36
|
391 |
make_active(G.head(j));
|
athos@36
|
392 |
}
|
athos@36
|
393 |
modify_preflow(j,v);
|
athos@36
|
394 |
}
|
athos@331
|
395 |
G.next(j);
|
athos@36
|
396 |
}
|
athos@36
|
397 |
}
|
athos@36
|
398 |
//In edges to node a
|
athos@36
|
399 |
{
|
athos@77
|
400 |
InEdgeIt j=G.template first<InEdgeIt>(a);
|
athos@331
|
401 |
while (G.valid(j) && e){
|
athos@36
|
402 |
if (is_admissible_backward_edge(j,new_level)){
|
athos@331
|
403 |
v=min(e,preflow[j]);
|
athos@36
|
404 |
e -= v;
|
athos@36
|
405 |
//New node might become active
|
athos@331
|
406 |
if (excess[G.tail(j)]==0){
|
athos@36
|
407 |
make_active(G.tail(j));
|
athos@36
|
408 |
}
|
athos@36
|
409 |
modify_preflow(j,-v);
|
athos@36
|
410 |
}
|
athos@331
|
411 |
G.next(j);
|
athos@36
|
412 |
}
|
athos@36
|
413 |
}
|
athos@36
|
414 |
|
athos@119
|
415 |
//if (G.id(a)==999)
|
athos@119
|
416 |
//cout<<new_level<<" e: "<<e<<endl;
|
athos@36
|
417 |
//cout<<G.id(a)<<" "<<new_level<<endl;
|
athos@36
|
418 |
|
athos@36
|
419 |
if (0==e){
|
athos@36
|
420 |
//Saturating push
|
athos@36
|
421 |
go_to_next_node=true;
|
athos@36
|
422 |
}
|
athos@36
|
423 |
else{//If there is still excess in node a
|
athos@77
|
424 |
|
athos@77
|
425 |
//change_level_to(a,new_level+1);
|
athos@77
|
426 |
|
athos@36
|
427 |
//Level remains empty
|
athos@331
|
428 |
if (num_of_nodes_on_level[level[a]]==1){
|
athos@36
|
429 |
change_level_to(a,number_of_nodes);
|
athos@36
|
430 |
//go_to_next_node=True;
|
athos@36
|
431 |
}
|
athos@36
|
432 |
else{
|
athos@36
|
433 |
change_level_to(a,new_level+1);
|
athos@36
|
434 |
//increase_level(a);
|
athos@36
|
435 |
}
|
athos@77
|
436 |
|
athos@36
|
437 |
|
athos@36
|
438 |
|
athos@36
|
439 |
|
athos@36
|
440 |
switch(node_examination){
|
athos@36
|
441 |
case examine_to_relabel:
|
athos@36
|
442 |
make_active(a);
|
athos@36
|
443 |
|
athos@36
|
444 |
go_to_next_node = true;
|
athos@36
|
445 |
break;
|
athos@36
|
446 |
default:
|
athos@36
|
447 |
break;
|
athos@36
|
448 |
}
|
athos@36
|
449 |
|
athos@36
|
450 |
|
athos@36
|
451 |
|
athos@36
|
452 |
}//if (0==e)
|
athos@36
|
453 |
}
|
athos@36
|
454 |
}
|
athos@331
|
455 |
maxflow_value = excess[t];
|
athos@36
|
456 |
return maxflow_value;
|
athos@36
|
457 |
}//run
|
athos@36
|
458 |
|
athos@36
|
459 |
|
alpar@105
|
460 |
}//namespace hugo
|
athos@36
|
461 |
|
athos@36
|
462 |
#endif //PREFLOW_PUSH_HH
|