1 | <?xml version="1.0" standalone="no"?> |
---|
2 | <!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" |
---|
3 | "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [ |
---|
4 | |
---|
5 | ]> |
---|
6 | |
---|
7 | <section id="vorbis-spec-residue"> |
---|
8 | <sectioninfo> |
---|
9 | <releaseinfo> |
---|
10 | $Id: 08-residue.xml 13159 2007-06-21 05:22:35Z xiphmont $ |
---|
11 | </releaseinfo> |
---|
12 | </sectioninfo> |
---|
13 | <title>Residue setup and decode</title> |
---|
14 | |
---|
15 | |
---|
16 | <section> |
---|
17 | <title>Overview</title> |
---|
18 | |
---|
19 | <para> |
---|
20 | A residue vector represents the fine detail of the audio spectrum of |
---|
21 | one channel in an audio frame after the encoder subtracts the floor |
---|
22 | curve and performs any channel coupling. A residue vector may |
---|
23 | represent spectral lines, spectral magnitude, spectral phase or |
---|
24 | hybrids as mixed by channel coupling. The exact semantic content of |
---|
25 | the vector does not matter to the residue abstraction.</para> |
---|
26 | |
---|
27 | <para> |
---|
28 | Whatever the exact qualities, the Vorbis residue abstraction codes the |
---|
29 | residue vectors into the bitstream packet, and then reconstructs the |
---|
30 | vectors during decode. Vorbis makes use of three different encoding |
---|
31 | variants (numbered 0, 1 and 2) of the same basic vector encoding |
---|
32 | abstraction.</para> |
---|
33 | |
---|
34 | </section> |
---|
35 | |
---|
36 | <section> |
---|
37 | <title>Residue format</title> |
---|
38 | |
---|
39 | <para> |
---|
40 | Residue format partitions each vector in the vector bundle into chunks, |
---|
41 | classifies each chunk, encodes the chunk classifications and finally |
---|
42 | encodes the chunks themselves using the the specific VQ arrangement |
---|
43 | defined for each selected classification. |
---|
44 | The exact interleaving and partitioning vary by residue encoding number, |
---|
45 | however the high-level process used to classify and encode the residue |
---|
46 | vector is the same in all three variants.</para> |
---|
47 | |
---|
48 | <para> |
---|
49 | A set of coded residue vectors are all of the same length. High level |
---|
50 | coding structure, ignoring for the moment exactly how a partition is |
---|
51 | encoded and simply trusting that it is, is as follows:</para> |
---|
52 | |
---|
53 | <para> |
---|
54 | <itemizedlist> |
---|
55 | <listitem><para>Each vector is partitioned into multiple equal sized chunks |
---|
56 | according to configuration specified. If we have a vector size of |
---|
57 | <emphasis>n</emphasis>, a partition size <emphasis>residue_partition_size</emphasis>, and a total |
---|
58 | of <emphasis>ch</emphasis> residue vectors, the total number of partitioned chunks |
---|
59 | coded is <emphasis>n</emphasis>/<emphasis>residue_partition_size</emphasis>*<emphasis>ch</emphasis>. It is |
---|
60 | important to note that the integer division truncates. In the below |
---|
61 | example, we assume an example <emphasis>residue_partition_size</emphasis> of 8.</para></listitem> |
---|
62 | |
---|
63 | <listitem><para>Each partition in each vector has a classification number that |
---|
64 | specifies which of multiple configured VQ codebook setups are used to |
---|
65 | decode that partition. The classification numbers of each partition |
---|
66 | can be thought of as forming a vector in their own right, as in the |
---|
67 | illustration below. Just as the residue vectors are coded in grouped |
---|
68 | partitions to increase encoding efficiency, the classification vector |
---|
69 | is also partitioned into chunks. The integer elements of each scalar |
---|
70 | in a classification chunk are built into a single scalar that |
---|
71 | represents the classification numbers in that chunk. In the below |
---|
72 | example, the classification codeword encodes two classification |
---|
73 | numbers.</para></listitem> |
---|
74 | |
---|
75 | <listitem><para>The values in a residue vector may be encoded monolithically in a |
---|
76 | single pass through the residue vector, but more often efficient |
---|
77 | codebook design dictates that each vector is encoded as the additive |
---|
78 | sum of several passes through the residue vector using more than one |
---|
79 | VQ codebook. Thus, each residue value potentially accumulates values |
---|
80 | from multiple decode passes. The classification value associated with |
---|
81 | a partition is the same in each pass, thus the classification codeword |
---|
82 | is coded only in the first pass.</para></listitem> |
---|
83 | |
---|
84 | </itemizedlist> |
---|
85 | </para> |
---|
86 | |
---|
87 | <mediaobject> |
---|
88 | <imageobject> |
---|
89 | <imagedata fileref="residue-pack.png" format="PNG"/> |
---|
90 | </imageobject> |
---|
91 | <textobject> |
---|
92 | <phrase>[illustration of residue vector format]</phrase> |
---|
93 | </textobject> |
---|
94 | </mediaobject> |
---|
95 | |
---|
96 | </section> |
---|
97 | |
---|
98 | <section><title>residue 0</title> |
---|
99 | |
---|
100 | <para> |
---|
101 | Residue 0 and 1 differ only in the way the values within a residue |
---|
102 | partition are interleaved during partition encoding (visually treated |
---|
103 | as a black box--or cyan box or brown box--in the above figure).</para> |
---|
104 | |
---|
105 | <para> |
---|
106 | Residue encoding 0 interleaves VQ encoding according to the |
---|
107 | dimension of the codebook used to encode a partition in a specific |
---|
108 | pass. The dimension of the codebook need not be the same in multiple |
---|
109 | passes, however the partition size must be an even multiple of the |
---|
110 | codebook dimension.</para> |
---|
111 | |
---|
112 | <para> |
---|
113 | As an example, assume a partition vector of size eight, to be encoded |
---|
114 | by residue 0 using codebook sizes of 8, 4, 2 and 1:</para> |
---|
115 | |
---|
116 | <programlisting> |
---|
117 | |
---|
118 | original residue vector: [ 0 1 2 3 4 5 6 7 ] |
---|
119 | |
---|
120 | codebook dimensions = 8 encoded as: [ 0 1 2 3 4 5 6 7 ] |
---|
121 | |
---|
122 | codebook dimensions = 4 encoded as: [ 0 2 4 6 ], [ 1 3 5 7 ] |
---|
123 | |
---|
124 | codebook dimensions = 2 encoded as: [ 0 4 ], [ 1 5 ], [ 2 6 ], [ 3 7 ] |
---|
125 | |
---|
126 | codebook dimensions = 1 encoded as: [ 0 ], [ 1 ], [ 2 ], [ 3 ], [ 4 ], [ 5 ], [ 6 ], [ 7 ] |
---|
127 | |
---|
128 | </programlisting> |
---|
129 | |
---|
130 | <para> |
---|
131 | It is worth mentioning at this point that no configurable value in the |
---|
132 | residue coding setup is restricted to a power of two.</para> |
---|
133 | |
---|
134 | </section> |
---|
135 | |
---|
136 | <section><title>residue 1</title> |
---|
137 | |
---|
138 | <para> |
---|
139 | Residue 1 does not interleave VQ encoding. It represents partition |
---|
140 | vector scalars in order. As with residue 0, however, partition length |
---|
141 | must be an integer multiple of the codebook dimension, although |
---|
142 | dimension may vary from pass to pass.</para> |
---|
143 | |
---|
144 | <para> |
---|
145 | As an example, assume a partition vector of size eight, to be encoded |
---|
146 | by residue 0 using codebook sizes of 8, 4, 2 and 1:</para> |
---|
147 | |
---|
148 | <programlisting> |
---|
149 | |
---|
150 | original residue vector: [ 0 1 2 3 4 5 6 7 ] |
---|
151 | |
---|
152 | codebook dimensions = 8 encoded as: [ 0 1 2 3 4 5 6 7 ] |
---|
153 | |
---|
154 | codebook dimensions = 4 encoded as: [ 0 1 2 3 ], [ 4 5 6 7 ] |
---|
155 | |
---|
156 | codebook dimensions = 2 encoded as: [ 0 1 ], [ 2 3 ], [ 4 5 ], [ 6 7 ] |
---|
157 | |
---|
158 | codebook dimensions = 1 encoded as: [ 0 ], [ 1 ], [ 2 ], [ 3 ], [ 4 ], [ 5 ], [ 6 ], [ 7 ] |
---|
159 | |
---|
160 | </programlisting> |
---|
161 | |
---|
162 | </section> |
---|
163 | |
---|
164 | <section><title>residue 2</title> |
---|
165 | |
---|
166 | <para> |
---|
167 | Residue type two can be thought of as a variant of residue type 1. |
---|
168 | Rather than encoding multiple passed-in vectors as in residue type 1, |
---|
169 | the <emphasis>ch</emphasis> passed in vectors of length <emphasis>n</emphasis> are first |
---|
170 | interleaved and flattened into a single vector of length |
---|
171 | <emphasis>ch</emphasis>*<emphasis>n</emphasis>. Encoding then proceeds as in type 1. Decoding is |
---|
172 | as in type 1 with decode interleave reversed. If operating on a single |
---|
173 | vector to begin with, residue type 1 and type 2 are equivalent.</para> |
---|
174 | |
---|
175 | <mediaobject> |
---|
176 | <imageobject> |
---|
177 | <imagedata fileref="residue2.png" format="PNG"/> |
---|
178 | </imageobject> |
---|
179 | <textobject> |
---|
180 | <phrase>[illustration of residue type 2]</phrase> |
---|
181 | </textobject> |
---|
182 | </mediaobject> |
---|
183 | |
---|
184 | </section> |
---|
185 | |
---|
186 | <section> |
---|
187 | <title>Residue decode</title> |
---|
188 | |
---|
189 | <section><title>header decode</title> |
---|
190 | |
---|
191 | <para> |
---|
192 | Header decode for all three residue types is identical.</para> |
---|
193 | <programlisting> |
---|
194 | 1) [residue_begin] = read 24 bits as unsigned integer |
---|
195 | 2) [residue_end] = read 24 bits as unsigned integer |
---|
196 | 3) [residue_partition_size] = read 24 bits as unsigned integer and add one |
---|
197 | 4) [residue_classifications] = read 6 bits as unsigned integer and add one |
---|
198 | 5) [residue_classbook] = read 8 bits as unsigned integer |
---|
199 | </programlisting> |
---|
200 | |
---|
201 | <para> |
---|
202 | <varname>[residue_begin]</varname> and <varname>[residue_end]</varname> select the specific |
---|
203 | sub-portion of each vector that is actually coded; it implements akin |
---|
204 | to a bandpass where, for coding purposes, the vector effectively |
---|
205 | begins at element <varname>[residue_begin]</varname> and ends at |
---|
206 | <varname>[residue_end]</varname>. Preceding and following values in the unpacked |
---|
207 | vectors are zeroed. Note that for residue type 2, these values as |
---|
208 | well as <varname>[residue_partition_size]</varname>apply to the interleaved |
---|
209 | vector, not the individual vectors before interleave. |
---|
210 | <varname>[residue_partition_size]</varname> is as explained above, |
---|
211 | <varname>[residue_classifications]</varname> is the number of possible |
---|
212 | classification to which a partition can belong and |
---|
213 | <varname>[residue_classbook]</varname> is the codebook number used to code |
---|
214 | classification codewords. The number of dimensions in book |
---|
215 | <varname>[residue_classbook]</varname> determines how many classification values |
---|
216 | are grouped into a single classification codeword.</para> |
---|
217 | |
---|
218 | <para> |
---|
219 | Next we read a bitmap pattern that specifies which partition classes |
---|
220 | code values in which passes.</para> |
---|
221 | |
---|
222 | <programlisting> |
---|
223 | 1) iterate [i] over the range 0 ... [residue_classifications]-1 { |
---|
224 | |
---|
225 | 2) [high_bits] = 0 |
---|
226 | 3) [low_bits] = read 3 bits as unsigned integer |
---|
227 | 4) [bitflag] = read one bit as boolean |
---|
228 | 5) if ( [bitflag] is set ) then [high_bits] = read five bits as unsigned integer |
---|
229 | 6) vector [residue_cascade] element [i] = [high_bits] * 8 + [low_bits] |
---|
230 | } |
---|
231 | 7) done |
---|
232 | </programlisting> |
---|
233 | |
---|
234 | <para> |
---|
235 | Finally, we read in a list of book numbers, each corresponding to |
---|
236 | specific bit set in the cascade bitmap. We loop over the possible |
---|
237 | codebook classifications and the maximum possible number of encoding |
---|
238 | stages (8 in Vorbis I, as constrained by the elements of the cascade |
---|
239 | bitmap being eight bits):</para> |
---|
240 | |
---|
241 | <programlisting> |
---|
242 | 1) iterate [i] over the range 0 ... [residue_classifications]-1 { |
---|
243 | |
---|
244 | 2) iterate [j] over the range 0 ... 7 { |
---|
245 | |
---|
246 | 3) if ( vector [residue_cascade] element [i] bit [j] is set ) { |
---|
247 | |
---|
248 | 4) array [residue_books] element [i][j] = read 8 bits as unsigned integer |
---|
249 | |
---|
250 | } else { |
---|
251 | |
---|
252 | 5) array [residue_books] element [i][j] = unused |
---|
253 | |
---|
254 | } |
---|
255 | } |
---|
256 | } |
---|
257 | |
---|
258 | 6) done |
---|
259 | </programlisting> |
---|
260 | |
---|
261 | <para> |
---|
262 | An end-of-packet condition at any point in header decode renders the |
---|
263 | stream undecodable. In addition, any codebook number greater than the |
---|
264 | maximum numbered codebook set up in this stream also renders the |
---|
265 | stream undecodable.</para> |
---|
266 | |
---|
267 | </section> |
---|
268 | |
---|
269 | <section><title>packet decode</title> |
---|
270 | |
---|
271 | <para> |
---|
272 | Format 0 and 1 packet decode is identical except for specific |
---|
273 | partition interleave. Format 2 packet decode can be built out of the |
---|
274 | format 1 decode process. Thus we describe first the decode |
---|
275 | infrastructure identical to all three formats.</para> |
---|
276 | |
---|
277 | <para> |
---|
278 | In addition to configuration information, the residue decode process |
---|
279 | is passed the number of vectors in the submap bundle and a vector of |
---|
280 | flags indicating if any of the vectors are not to be decoded. If the |
---|
281 | passed in number of vectors is 3 and vector number 1 is marked 'do not |
---|
282 | decode', decode skips vector 1 during the decode loop. However, even |
---|
283 | 'do not decode' vectors are allocated and zeroed.</para> |
---|
284 | |
---|
285 | <para> |
---|
286 | Depending on the values of <varname>[residue_begin]</varname> and |
---|
287 | <varname>[residue_end]</varname>, it is obvious that the encoded |
---|
288 | portion of a residue vector may be the entire possible residue vector |
---|
289 | or some other strict subset of the actual residue vector size with |
---|
290 | zero padding at either uncoded end. However, it is also possible to |
---|
291 | set <varname>[residue_begin]</varname> and |
---|
292 | <varname>[residue_end]</varname> to specify a range partially or |
---|
293 | wholly beyond the maximum vector size. Before beginning residue |
---|
294 | decode, limit <varname>[residue_begin]</varname> and |
---|
295 | <varname>[residue_end]</varname> to the maximum possible vector size |
---|
296 | as follows. We assume that the number of vectors being encoded, |
---|
297 | <varname>[ch]</varname> is provided by the higher level decoding |
---|
298 | process.</para> |
---|
299 | |
---|
300 | <programlisting> |
---|
301 | 1) [actual_size] = current blocksize/2; |
---|
302 | 2) if residue encoding is format 2 |
---|
303 | 3) [actual_size] = [actual_size] * [ch]; |
---|
304 | 4) [limit_residue_begin] = maximum of ([residue_begin],[actual_size]); |
---|
305 | 5) [limit_residue_end] = maximum of ([residue_end],[actual_size]); |
---|
306 | </programlisting> |
---|
307 | |
---|
308 | <para> |
---|
309 | The following convenience values are conceptually useful to clarifying |
---|
310 | the decode process:</para> |
---|
311 | |
---|
312 | <programlisting> |
---|
313 | 1) [classwords_per_codeword] = [codebook_dimensions] value of codebook [residue_classbook] |
---|
314 | 2) [n_to_read] = [limit_residue_end] - [limit_residue_begin] |
---|
315 | 3) [partitions_to_read] = [n_to_read] / [residue_partition_size] |
---|
316 | </programlisting> |
---|
317 | |
---|
318 | <para> |
---|
319 | Packet decode proceeds as follows, matching the description offered earlier in the document. </para> |
---|
320 | <programlisting> |
---|
321 | 1) allocate and zero all vectors that will be returned. |
---|
322 | 2) if ([n_to_read] is zero), stop; there is no residue to decode. |
---|
323 | 3) iterate [pass] over the range 0 ... 7 { |
---|
324 | |
---|
325 | 4) [partition_count] = 0 |
---|
326 | |
---|
327 | 5) while [partition_count] is less than [partitions_to_read] |
---|
328 | |
---|
329 | 6) if ([pass] is zero) { |
---|
330 | |
---|
331 | 7) iterate [j] over the range 0 .. [ch]-1 { |
---|
332 | |
---|
333 | 8) if vector [j] is not marked 'do not decode' { |
---|
334 | |
---|
335 | 9) [temp] = read from packet using codebook [residue_classbook] in scalar context |
---|
336 | 10) iterate [i] descending over the range [classwords_per_codeword]-1 ... 0 { |
---|
337 | |
---|
338 | 11) array [classifications] element [j],([i]+[partition_count]) = |
---|
339 | [temp] integer modulo [residue_classifications] |
---|
340 | 12) [temp] = [temp] / [residue_classifications] using integer division |
---|
341 | |
---|
342 | } |
---|
343 | |
---|
344 | } |
---|
345 | |
---|
346 | } |
---|
347 | |
---|
348 | } |
---|
349 | |
---|
350 | 13) iterate [i] over the range 0 .. ([classwords_per_codeword] - 1) while [partition_count] |
---|
351 | is also less than [partitions_to_read] { |
---|
352 | |
---|
353 | 14) iterate [j] over the range 0 .. [ch]-1 { |
---|
354 | |
---|
355 | 15) if vector [j] is not marked 'do not decode' { |
---|
356 | |
---|
357 | 16) [vqclass] = array [classifications] element [j],[partition_count] |
---|
358 | 17) [vqbook] = array [residue_books] element [vqclass],[pass] |
---|
359 | 18) if ([vqbook] is not 'unused') { |
---|
360 | |
---|
361 | 19) decode partition into output vector number [j], starting at scalar |
---|
362 | offset [limit_residue_begin]+[partition_count]*[residue_partition_size] using |
---|
363 | codebook number [vqbook] in VQ context |
---|
364 | } |
---|
365 | } |
---|
366 | |
---|
367 | 20) increment [partition_count] by one |
---|
368 | |
---|
369 | } |
---|
370 | } |
---|
371 | } |
---|
372 | |
---|
373 | 21) done |
---|
374 | |
---|
375 | </programlisting> |
---|
376 | |
---|
377 | <para> |
---|
378 | An end-of-packet condition during packet decode is to be considered a |
---|
379 | nominal occurrence. Decode returns the result of vector decode up to |
---|
380 | that point.</para> |
---|
381 | |
---|
382 | </section> |
---|
383 | |
---|
384 | <section><title>format 0 specifics</title> |
---|
385 | |
---|
386 | <para> |
---|
387 | Format zero decodes partitions exactly as described earlier in the |
---|
388 | 'Residue Format: residue 0' section. The following pseudocode |
---|
389 | presents the same algorithm. Assume:</para> |
---|
390 | |
---|
391 | <para> |
---|
392 | <itemizedlist> |
---|
393 | <listitem><simpara> <varname>[n]</varname> is the value in <varname>[residue_partition_size]</varname></simpara></listitem> |
---|
394 | <listitem><simpara><varname>[v]</varname> is the residue vector</simpara></listitem> |
---|
395 | <listitem><simpara><varname>[offset]</varname> is the beginning read offset in [v]</simpara></listitem> |
---|
396 | </itemizedlist> |
---|
397 | </para> |
---|
398 | |
---|
399 | <programlisting> |
---|
400 | 1) [step] = [n] / [codebook_dimensions] |
---|
401 | 2) iterate [i] over the range 0 ... [step]-1 { |
---|
402 | |
---|
403 | 3) vector [entry_temp] = read vector from packet using current codebook in VQ context |
---|
404 | 4) iterate [j] over the range 0 ... [codebook_dimensions]-1 { |
---|
405 | |
---|
406 | 5) vector [v] element ([offset]+[i]+[j]*[step]) = |
---|
407 | vector [v] element ([offset]+[i]+[j]*[step]) + |
---|
408 | vector [entry_temp] element [j] |
---|
409 | |
---|
410 | } |
---|
411 | |
---|
412 | } |
---|
413 | |
---|
414 | 6) done |
---|
415 | |
---|
416 | </programlisting> |
---|
417 | |
---|
418 | </section> |
---|
419 | |
---|
420 | <section><title>format 1 specifics</title> |
---|
421 | |
---|
422 | <para> |
---|
423 | Format 1 decodes partitions exactly as described earlier in the |
---|
424 | 'Residue Format: residue 1' section. The following pseudocode |
---|
425 | presents the same algorithm. Assume:</para> |
---|
426 | |
---|
427 | <para> |
---|
428 | <itemizedlist> |
---|
429 | <listitem><simpara> <varname>[n]</varname> is the value in |
---|
430 | <varname>[residue_partition_size]</varname></simpara></listitem> |
---|
431 | <listitem><simpara><varname>[v]</varname> is the residue vector</simpara></listitem> |
---|
432 | <listitem><simpara><varname>[offset]</varname> is the beginning read offset in [v]</simpara></listitem> |
---|
433 | </itemizedlist> |
---|
434 | </para> |
---|
435 | |
---|
436 | <programlisting> |
---|
437 | 1) [i] = 0 |
---|
438 | 2) vector [entry_temp] = read vector from packet using current codebook in VQ context |
---|
439 | 3) iterate [j] over the range 0 ... [codebook_dimensions]-1 { |
---|
440 | |
---|
441 | 4) vector [v] element ([offset]+[i]) = |
---|
442 | vector [v] element ([offset]+[i]) + |
---|
443 | vector [entry_temp] element [j] |
---|
444 | 5) increment [i] |
---|
445 | |
---|
446 | } |
---|
447 | |
---|
448 | 6) if ( [i] is less than [n] ) continue at step 2 |
---|
449 | 7) done |
---|
450 | </programlisting> |
---|
451 | |
---|
452 | </section> |
---|
453 | |
---|
454 | <section><title>format 2 specifics</title> |
---|
455 | |
---|
456 | <para> |
---|
457 | Format 2 is reducible to format 1. It may be implemented as an additional step prior to and an additional post-decode step after a normal format 1 decode. |
---|
458 | </para> |
---|
459 | |
---|
460 | <para> |
---|
461 | Format 2 handles 'do not decode' vectors differently than residue 0 or |
---|
462 | 1; if all vectors are marked 'do not decode', no decode occurrs. |
---|
463 | However, if at least one vector is to be decoded, all the vectors are |
---|
464 | decoded. We then request normal format 1 to decode a single vector |
---|
465 | representing all output channels, rather than a vector for each |
---|
466 | channel. After decode, deinterleave the vector into independent vectors, one for each output channel. That is:</para> |
---|
467 | |
---|
468 | <orderedlist> |
---|
469 | <listitem><simpara>If all vectors 0 through <emphasis>ch</emphasis>-1 are marked 'do not decode', allocate and clear a single vector <varname>[v]</varname>of length <emphasis>ch*n</emphasis> and skip step 2 below; proceed directly to the post-decode step.</simpara></listitem> |
---|
470 | <listitem><simpara>Rather than performing format 1 decode to produce <emphasis>ch</emphasis> vectors of length <emphasis>n</emphasis> each, call format 1 decode to produce a single vector <varname>[v]</varname> of length <emphasis>ch*n</emphasis>. </simpara></listitem> |
---|
471 | <listitem><para>Post decode: Deinterleave the single vector <varname>[v]</varname> returned by format 1 decode as described above into <emphasis>ch</emphasis> independent vectors, one for each outputchannel, according to: |
---|
472 | <programlisting> |
---|
473 | 1) iterate [i] over the range 0 ... [n]-1 { |
---|
474 | |
---|
475 | 2) iterate [j] over the range 0 ... [ch]-1 { |
---|
476 | |
---|
477 | 3) output vector number [j] element [i] = vector [v] element ([i] * [ch] + [j]) |
---|
478 | |
---|
479 | } |
---|
480 | } |
---|
481 | |
---|
482 | 4) done |
---|
483 | </programlisting> |
---|
484 | </para></listitem> |
---|
485 | </orderedlist> |
---|
486 | |
---|
487 | </section> |
---|
488 | |
---|
489 | </section> |
---|
490 | |
---|
491 | </section> |
---|
492 | |
---|