]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | <?xml version="1.0" encoding="utf-8" ?> |
2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
3 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |
4 | <head> | |
5 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
6 | <meta name="generator" content="Docutils 0.5: http://docutils.sourceforge.net/" /> | |
7 | <title>Zip Iterator</title> | |
8 | <meta name="author" content="David Abrahams, Thomas Becker" /> | |
9 | <meta name="organization" content="Boost Consulting, Zephyr Associates, Inc." /> | |
10 | <meta name="date" content="2006-09-11" /> | |
11 | <meta name="copyright" content="Copyright David Abrahams and Thomas Becker 2003." /> | |
12 | <link rel="stylesheet" href="../../../rst.css" type="text/css" /> | |
13 | </head> | |
14 | <body> | |
15 | <div class="document" id="zip-iterator"> | |
16 | <h1 class="title">Zip Iterator</h1> | |
17 | <table class="docinfo" frame="void" rules="none"> | |
18 | <col class="docinfo-name" /> | |
19 | <col class="docinfo-content" /> | |
20 | <tbody valign="top"> | |
21 | <tr><th class="docinfo-name">Author:</th> | |
22 | <td>David Abrahams, Thomas Becker</td></tr> | |
23 | <tr><th class="docinfo-name">Contact:</th> | |
24 | <td><a class="first reference external" href="mailto:dave@boost-consulting.com">dave@boost-consulting.com</a>, <a class="last reference external" href="mailto:thomas@styleadvisor.com">thomas@styleadvisor.com</a></td></tr> | |
25 | <tr><th class="docinfo-name">Organization:</th> | |
26 | <td><a class="first reference external" href="http://www.boost-consulting.com">Boost Consulting</a>, <a class="last reference external" href="http://www.styleadvisor.com">Zephyr Associates, Inc.</a></td></tr> | |
27 | <tr><th class="docinfo-name">Date:</th> | |
28 | <td>2006-09-11</td></tr> | |
29 | <tr><th class="docinfo-name">Copyright:</th> | |
30 | <td>Copyright David Abrahams and Thomas Becker 2003.</td></tr> | |
31 | </tbody> | |
32 | </table> | |
33 | <!-- Distributed under the Boost --> | |
34 | <!-- Software License, Version 1.0. (See accompanying --> | |
35 | <!-- file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) --> | |
36 | <table class="docutils field-list" frame="void" rules="none"> | |
37 | <col class="field-name" /> | |
38 | <col class="field-body" /> | |
39 | <tbody valign="top"> | |
40 | <tr class="field"><th class="field-name">abstract:</th><td class="field-body"><!-- Copyright David Abrahams 2006. Distributed under the Boost --> | |
41 | <!-- Software License, Version 1.0. (See accompanying --> | |
42 | <!-- file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) --> | |
43 | The zip iterator provides the ability to parallel-iterate | |
44 | over several controlled sequences simultaneously. A zip | |
45 | iterator is constructed from a tuple of iterators. Moving | |
46 | the zip iterator moves all the iterators in parallel. | |
47 | Dereferencing the zip iterator returns a tuple that contains | |
48 | the results of dereferencing the individual iterators.</td> | |
49 | </tr> | |
50 | </tbody> | |
51 | </table> | |
52 | <div class="contents topic" id="table-of-contents"> | |
53 | <p class="topic-title first">Table of Contents</p> | |
54 | <ul class="simple"> | |
55 | <li><a class="reference internal" href="#zip-iterator-synopsis" id="id1"><tt class="docutils literal"><span class="pre">zip_iterator</span></tt> synopsis</a></li> | |
56 | <li><a class="reference internal" href="#zip-iterator-requirements" id="id2"><tt class="docutils literal"><span class="pre">zip_iterator</span></tt> requirements</a></li> | |
57 | <li><a class="reference internal" href="#zip-iterator-models" id="id3"><tt class="docutils literal"><span class="pre">zip_iterator</span></tt> models</a></li> | |
58 | <li><a class="reference internal" href="#zip-iterator-operations" id="id4"><tt class="docutils literal"><span class="pre">zip_iterator</span></tt> operations</a></li> | |
59 | <li><a class="reference internal" href="#examples" id="id5">Examples</a></li> | |
60 | </ul> | |
61 | </div> | |
62 | <div class="section" id="zip-iterator-synopsis"> | |
63 | <h1><a class="toc-backref" href="#id1"><tt class="docutils literal"><span class="pre">zip_iterator</span></tt> synopsis</a></h1> | |
64 | <!-- Copyright David Abrahams 2006. Distributed under the Boost --> | |
65 | <!-- Software License, Version 1.0. (See accompanying --> | |
66 | <!-- file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) --> | |
67 | <pre class="literal-block"> | |
68 | template<typename IteratorTuple> | |
69 | class zip_iterator | |
70 | { | |
71 | ||
72 | public: | |
73 | typedef /* see below */ reference; | |
74 | typedef reference value_type; | |
75 | typedef value_type* pointer; | |
76 | typedef /* see below */ difference_type; | |
77 | typedef /* see below */ iterator_category; | |
78 | ||
79 | zip_iterator(); | |
80 | zip_iterator(IteratorTuple iterator_tuple); | |
81 | ||
82 | template<typename OtherIteratorTuple> | |
83 | zip_iterator( | |
84 | const zip_iterator<OtherIteratorTuple>& other | |
85 | , typename enable_if_convertible< | |
86 | OtherIteratorTuple | |
87 | , IteratorTuple>::type* = 0 // exposition only | |
88 | ); | |
89 | ||
90 | const IteratorTuple& get_iterator_tuple() const; | |
91 | ||
92 | private: | |
93 | IteratorTuple m_iterator_tuple; // exposition only | |
94 | }; | |
95 | ||
96 | template<typename IteratorTuple> | |
97 | zip_iterator<IteratorTuple> | |
98 | make_zip_iterator(IteratorTuple t); | |
99 | </pre> | |
100 | <p>The <tt class="docutils literal"><span class="pre">reference</span></tt> member of <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> is the type of the tuple | |
101 | made of the reference types of the iterator types in the <tt class="docutils literal"><span class="pre">IteratorTuple</span></tt> | |
102 | argument.</p> | |
103 | <p>The <tt class="docutils literal"><span class="pre">difference_type</span></tt> member of <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> is the <tt class="docutils literal"><span class="pre">difference_type</span></tt> | |
104 | of the first of the iterator types in the <tt class="docutils literal"><span class="pre">IteratorTuple</span></tt> argument.</p> | |
105 | <p>The <tt class="docutils literal"><span class="pre">iterator_category</span></tt> member of <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> is convertible to the | |
106 | minimum of the traversal categories of the iterator types in the <tt class="docutils literal"><span class="pre">IteratorTuple</span></tt> | |
107 | argument. For example, if the <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> holds only vector | |
108 | iterators, then <tt class="docutils literal"><span class="pre">iterator_category</span></tt> is convertible to | |
109 | <tt class="docutils literal"><span class="pre">boost::random_access_traversal_tag</span></tt>. If you add a list iterator, then | |
110 | <tt class="docutils literal"><span class="pre">iterator_category</span></tt> will be convertible to <tt class="docutils literal"><span class="pre">boost::bidirectional_traversal_tag</span></tt>, | |
111 | but no longer to <tt class="docutils literal"><span class="pre">boost::random_access_traversal_tag</span></tt>.</p> | |
112 | </div> | |
113 | <div class="section" id="zip-iterator-requirements"> | |
114 | <h1><a class="toc-backref" href="#id2"><tt class="docutils literal"><span class="pre">zip_iterator</span></tt> requirements</a></h1> | |
115 | <p>All iterator types in the argument <tt class="docutils literal"><span class="pre">IteratorTuple</span></tt> shall model Readable Iterator.</p> | |
116 | </div> | |
117 | <div class="section" id="zip-iterator-models"> | |
118 | <h1><a class="toc-backref" href="#id3"><tt class="docutils literal"><span class="pre">zip_iterator</span></tt> models</a></h1> | |
119 | <p>The resulting <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> models Readable Iterator.</p> | |
120 | <p>The fact that the <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> models only Readable Iterator does not | |
121 | prevent you from modifying the values that the individual iterators point | |
122 | to. The tuple returned by the <tt class="docutils literal"><span class="pre">zip_iterator</span></tt>'s <tt class="docutils literal"><span class="pre">operator*</span></tt> is a tuple | |
123 | constructed from the reference types of the individual iterators, not | |
124 | their value types. For example, if <tt class="docutils literal"><span class="pre">zip_it</span></tt> is a <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> whose | |
125 | first member iterator is an <tt class="docutils literal"><span class="pre">std::vector<double>::iterator</span></tt>, then the | |
126 | following line will modify the value which the first member iterator of | |
127 | <tt class="docutils literal"><span class="pre">zip_it</span></tt> currently points to:</p> | |
128 | <pre class="literal-block"> | |
129 | zip_it->get<0>() = 42.0; | |
130 | </pre> | |
131 | <p>Consider the set of standard traversal concepts obtained by taking | |
132 | the most refined standard traversal concept modeled by each individual | |
133 | iterator type in the <tt class="docutils literal"><span class="pre">IteratorTuple</span></tt> argument.The <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> | |
134 | models the least refined standard traversal concept in this set.</p> | |
135 | <p><tt class="docutils literal"><span class="pre">zip_iterator<IteratorTuple1></span></tt> is interoperable with | |
136 | <tt class="docutils literal"><span class="pre">zip_iterator<IteratorTuple2></span></tt> if and only if <tt class="docutils literal"><span class="pre">IteratorTuple1</span></tt> | |
137 | is interoperable with <tt class="docutils literal"><span class="pre">IteratorTuple2</span></tt>.</p> | |
138 | </div> | |
139 | <div class="section" id="zip-iterator-operations"> | |
140 | <h1><a class="toc-backref" href="#id4"><tt class="docutils literal"><span class="pre">zip_iterator</span></tt> operations</a></h1> | |
141 | <p>In addition to the operations required by the concepts modeled by | |
142 | <tt class="docutils literal"><span class="pre">zip_iterator</span></tt>, <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> provides the following | |
143 | operations.</p> | |
144 | <p><tt class="docutils literal"><span class="pre">zip_iterator();</span></tt></p> | |
145 | <table class="docutils field-list" frame="void" rules="none"> | |
146 | <col class="field-name" /> | |
147 | <col class="field-body" /> | |
148 | <tbody valign="top"> | |
149 | <tr class="field"><th class="field-name">Returns:</th><td class="field-body">An instance of <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> with <tt class="docutils literal"><span class="pre">m_iterator_tuple</span></tt> | |
150 | default constructed.</td> | |
151 | </tr> | |
152 | </tbody> | |
153 | </table> | |
154 | <p><tt class="docutils literal"><span class="pre">zip_iterator(IteratorTuple</span> <span class="pre">iterator_tuple);</span></tt></p> | |
155 | <table class="docutils field-list" frame="void" rules="none"> | |
156 | <col class="field-name" /> | |
157 | <col class="field-body" /> | |
158 | <tbody valign="top"> | |
159 | <tr class="field"><th class="field-name">Returns:</th><td class="field-body">An instance of <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> with <tt class="docutils literal"><span class="pre">m_iterator_tuple</span></tt> | |
160 | initialized to <tt class="docutils literal"><span class="pre">iterator_tuple</span></tt>.</td> | |
161 | </tr> | |
162 | </tbody> | |
163 | </table> | |
164 | <pre class="literal-block"> | |
165 | template<typename OtherIteratorTuple> | |
166 | zip_iterator( | |
167 | const zip_iterator<OtherIteratorTuple>& other | |
168 | , typename enable_if_convertible< | |
169 | OtherIteratorTuple | |
170 | , IteratorTuple>::type* = 0 // exposition only | |
171 | ); | |
172 | </pre> | |
173 | <table class="docutils field-list" frame="void" rules="none"> | |
174 | <col class="field-name" /> | |
175 | <col class="field-body" /> | |
176 | <tbody valign="top"> | |
177 | <tr class="field"><th class="field-name">Returns:</th><td class="field-body">An instance of <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> that is a copy of <tt class="docutils literal"><span class="pre">other</span></tt>.</td> | |
178 | </tr> | |
179 | <tr class="field"><th class="field-name">Requires:</th><td class="field-body"><tt class="docutils literal"><span class="pre">OtherIteratorTuple</span></tt> is implicitly convertible to <tt class="docutils literal"><span class="pre">IteratorTuple</span></tt>.</td> | |
180 | </tr> | |
181 | </tbody> | |
182 | </table> | |
183 | <p><tt class="docutils literal"><span class="pre">const</span> <span class="pre">IteratorTuple&</span> <span class="pre">get_iterator_tuple()</span> <span class="pre">const;</span></tt></p> | |
184 | <table class="docutils field-list" frame="void" rules="none"> | |
185 | <col class="field-name" /> | |
186 | <col class="field-body" /> | |
187 | <tbody valign="top"> | |
188 | <tr class="field"><th class="field-name">Returns:</th><td class="field-body"><tt class="docutils literal"><span class="pre">m_iterator_tuple</span></tt></td> | |
189 | </tr> | |
190 | </tbody> | |
191 | </table> | |
192 | <p><tt class="docutils literal"><span class="pre">reference</span> <span class="pre">operator*()</span> <span class="pre">const;</span></tt></p> | |
193 | <table class="docutils field-list" frame="void" rules="none"> | |
194 | <col class="field-name" /> | |
195 | <col class="field-body" /> | |
196 | <tbody valign="top"> | |
197 | <tr class="field"><th class="field-name">Returns:</th><td class="field-body">A tuple consisting of the results of dereferencing all iterators in | |
198 | <tt class="docutils literal"><span class="pre">m_iterator_tuple</span></tt>.</td> | |
199 | </tr> | |
200 | </tbody> | |
201 | </table> | |
202 | <p><tt class="docutils literal"><span class="pre">zip_iterator&</span> <span class="pre">operator++();</span></tt></p> | |
203 | <table class="docutils field-list" frame="void" rules="none"> | |
204 | <col class="field-name" /> | |
205 | <col class="field-body" /> | |
206 | <tbody valign="top"> | |
207 | <tr class="field"><th class="field-name">Effects:</th><td class="field-body">Increments each iterator in <tt class="docutils literal"><span class="pre">m_iterator_tuple</span></tt>.</td> | |
208 | </tr> | |
209 | <tr class="field"><th class="field-name">Returns:</th><td class="field-body"><tt class="docutils literal"><span class="pre">*this</span></tt></td> | |
210 | </tr> | |
211 | </tbody> | |
212 | </table> | |
213 | <p><tt class="docutils literal"><span class="pre">zip_iterator&</span> <span class="pre">operator--();</span></tt></p> | |
214 | <table class="docutils field-list" frame="void" rules="none"> | |
215 | <col class="field-name" /> | |
216 | <col class="field-body" /> | |
217 | <tbody valign="top"> | |
218 | <tr class="field"><th class="field-name">Effects:</th><td class="field-body">Decrements each iterator in <tt class="docutils literal"><span class="pre">m_iterator_tuple</span></tt>.</td> | |
219 | </tr> | |
220 | <tr class="field"><th class="field-name">Returns:</th><td class="field-body"><tt class="docutils literal"><span class="pre">*this</span></tt></td> | |
221 | </tr> | |
222 | </tbody> | |
223 | </table> | |
224 | <pre class="literal-block"> | |
225 | template<typename IteratorTuple> | |
226 | zip_iterator<IteratorTuple> | |
227 | make_zip_iterator(IteratorTuple t); | |
228 | </pre> | |
229 | <table class="docutils field-list" frame="void" rules="none"> | |
230 | <col class="field-name" /> | |
231 | <col class="field-body" /> | |
232 | <tbody valign="top"> | |
233 | <tr class="field"><th class="field-name">Returns:</th><td class="field-body">An instance of <tt class="docutils literal"><span class="pre">zip_iterator<IteratorTuple></span></tt> with <tt class="docutils literal"><span class="pre">m_iterator_tuple</span></tt> | |
234 | initialized to <tt class="docutils literal"><span class="pre">t</span></tt>.</td> | |
235 | </tr> | |
236 | </tbody> | |
237 | </table> | |
238 | <!-- Copyright David Abrahams 2006. Distributed under the Boost --> | |
239 | <!-- Software License, Version 1.0. (See accompanying --> | |
240 | <!-- file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) --> | |
241 | <pre class="literal-block"> | |
242 | template<typename IteratorTuple> | |
243 | zip_iterator<IteratorTuple> | |
244 | make_zip_iterator(IteratorTuple t); | |
245 | </pre> | |
246 | <table class="docutils field-list" frame="void" rules="none"> | |
247 | <col class="field-name" /> | |
248 | <col class="field-body" /> | |
249 | <tbody valign="top"> | |
250 | <tr class="field"><th class="field-name">Returns:</th><td class="field-body">An instance of <tt class="docutils literal"><span class="pre">zip_iterator<IteratorTuple></span></tt> with <tt class="docutils literal"><span class="pre">m_iterator_tuple</span></tt> | |
251 | initialized to <tt class="docutils literal"><span class="pre">t</span></tt>.</td> | |
252 | </tr> | |
253 | </tbody> | |
254 | </table> | |
255 | <!-- Copyright David Abrahams 2006. Distributed under the Boost --> | |
256 | <!-- Software License, Version 1.0. (See accompanying --> | |
257 | <!-- file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) --> | |
258 | </div> | |
259 | <div class="section" id="examples"> | |
260 | <h1><a class="toc-backref" href="#id5">Examples</a></h1> | |
261 | <p>There are two main types of applications of the <tt class="docutils literal"><span class="pre">zip_iterator</span></tt>. The first | |
262 | one concerns runtime efficiency: If one has several controlled sequences | |
263 | of the same length that must be somehow processed, e.g., with the | |
264 | <tt class="docutils literal"><span class="pre">for_each</span></tt> algorithm, then it is more efficient to perform just | |
265 | one parallel-iteration rather than several individual iterations. For an | |
266 | example, assume that <tt class="docutils literal"><span class="pre">vect_of_doubles</span></tt> and <tt class="docutils literal"><span class="pre">vect_of_ints</span></tt> | |
267 | are two vectors of equal length containing doubles and ints, respectively, | |
268 | and consider the following two iterations:</p> | |
269 | <pre class="literal-block"> | |
270 | std::vector<double>::const_iterator beg1 = vect_of_doubles.begin(); | |
271 | std::vector<double>::const_iterator end1 = vect_of_doubles.end(); | |
272 | std::vector<int>::const_iterator beg2 = vect_of_ints.begin(); | |
273 | std::vector<int>::const_iterator end2 = vect_of_ints.end(); | |
274 | ||
275 | std::for_each(beg1, end1, func_0()); | |
276 | std::for_each(beg2, end2, func_1()); | |
277 | </pre> | |
278 | <p>These two iterations can now be replaced with a single one as follows:</p> | |
279 | <pre class="literal-block"> | |
280 | std::for_each( | |
281 | boost::make_zip_iterator( | |
282 | boost::make_tuple(beg1, beg2) | |
283 | ), | |
284 | boost::make_zip_iterator( | |
285 | boost::make_tuple(end1, end2) | |
286 | ), | |
287 | zip_func() | |
288 | ); | |
289 | </pre> | |
290 | <p>A non-generic implementation of <tt class="docutils literal"><span class="pre">zip_func</span></tt> could look as follows:</p> | |
291 | <pre class="literal-block"> | |
292 | struct zip_func : | |
293 | public std::unary_function<const boost::tuple<const double&, const int&>&, void> | |
294 | { | |
295 | void operator()(const boost::tuple<const double&, const int&>& t) const | |
296 | { | |
297 | m_f0(t.get<0>()); | |
298 | m_f1(t.get<1>()); | |
299 | } | |
300 | ||
301 | private: | |
302 | func_0 m_f0; | |
303 | func_1 m_f1; | |
304 | }; | |
305 | </pre> | |
306 | <p>The second important application of the <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> is as a building block | |
307 | to make combining iterators. A combining iterator is an iterator | |
308 | that parallel-iterates over several controlled sequences and, upon | |
309 | dereferencing, returns the result of applying a functor to the values of the | |
310 | sequences at the respective positions. This can now be achieved by using the | |
311 | <tt class="docutils literal"><span class="pre">zip_iterator</span></tt> in conjunction with the <tt class="docutils literal"><span class="pre">transform_iterator</span></tt>.</p> | |
312 | <p>Suppose, for example, that you have two vectors of doubles, say | |
313 | <tt class="docutils literal"><span class="pre">vect_1</span></tt> and <tt class="docutils literal"><span class="pre">vect_2</span></tt>, and you need to expose to a client | |
314 | a controlled sequence containing the products of the elements of | |
315 | <tt class="docutils literal"><span class="pre">vect_1</span></tt> and <tt class="docutils literal"><span class="pre">vect_2</span></tt>. Rather than placing these products | |
316 | in a third vector, you can use a combining iterator that calculates the | |
317 | products on the fly. Let us assume that <tt class="docutils literal"><span class="pre">tuple_multiplies</span></tt> is a | |
318 | functor that works like <tt class="docutils literal"><span class="pre">std::multiplies</span></tt>, except that it takes | |
319 | its two arguments packaged in a tuple. Then the two iterators | |
320 | <tt class="docutils literal"><span class="pre">it_begin</span></tt> and <tt class="docutils literal"><span class="pre">it_end</span></tt> defined below delimit a controlled | |
321 | sequence containing the products of the elements of <tt class="docutils literal"><span class="pre">vect_1</span></tt> and | |
322 | <tt class="docutils literal"><span class="pre">vect_2</span></tt>:</p> | |
323 | <pre class="literal-block"> | |
324 | typedef boost::tuple< | |
325 | std::vector<double>::const_iterator, | |
326 | std::vector<double>::const_iterator | |
327 | > the_iterator_tuple; | |
328 | ||
329 | typedef boost::zip_iterator< | |
330 | the_iterator_tuple | |
331 | > the_zip_iterator; | |
332 | ||
333 | typedef boost::transform_iterator< | |
334 | tuple_multiplies<double>, | |
335 | the_zip_iterator | |
336 | > the_transform_iterator; | |
337 | ||
338 | the_transform_iterator it_begin( | |
339 | the_zip_iterator( | |
340 | the_iterator_tuple( | |
341 | vect_1.begin(), | |
342 | vect_2.begin() | |
343 | ) | |
344 | ), | |
345 | tuple_multiplies<double>() | |
346 | ); | |
347 | ||
348 | the_transform_iterator it_end( | |
349 | the_zip_iterator( | |
350 | the_iterator_tuple( | |
351 | vect_1.end(), | |
352 | vect_2.end() | |
353 | ) | |
354 | ), | |
355 | tuple_multiplies<double>() | |
356 | ); | |
357 | </pre> | |
358 | </div> | |
359 | </div> | |
360 | <div class="footer"> | |
361 | <hr class="footer" /> | |
362 | <a class="reference external" href="zip_iterator.rst">View document source</a>. | |
363 | Generated by <a class="reference external" href="http://docutils.sourceforge.net/">Docutils</a> from <a class="reference external" href="http://docutils.sourceforge.net/rst.html">reStructuredText</a> source. | |
364 | ||
365 | </div> | |
366 | </body> | |
367 | </html> |