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