]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/multi_array/doc/user.html
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / multi_array / doc / user.html
CommitLineData
7c673cae
FG
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
2 "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4<!--
5 == Copyright 2002 The Trustees of Indiana University.
6
7 == Use, modification and distribution is subject to the Boost Software
8 == License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
9 == http://www.boost.org/LICENSE_1_0.txt)
10
11 == Boost.MultiArray Library
12 == Authors: Ronald Garcia
13 == Jeremy Siek
14 == Andrew Lumsdaine
15 == See http://www.boost.org/libs/multi_array for documentation.
16 -->
17<head>
18 <title>The Boost Multidimensional Array Library (Boost.MultiArray)</title>
19</head>
20
21<body>
22
23<h1>
24 <img src="../../../boost.png" alt="boost logo"
25 width="277" align="middle" height="86">
26 <br>The Boost Multidimensional Array Library
27 <br>(Boost.MultiArray)
28</h1>
29
30<h2>Synopsis</h2>
31
32<p>
33The Boost Multidimensional Array Library provides a class template for
34multidimensional arrays, as well as semantically equivalent
35adaptors for arrays of contiguous data. The classes in this library
36implement a common interface, formalized as a generic programming
37concept. The interface design is in line with the precedent set by the
38C++ Standard Library containers. Boost MultiArray is a more efficient
39and convenient way to express N-dimensional arrays than existing
40alternatives (especially the
41<tt>std::vector&lt;std::vector&lt;...&gt;&gt;</tt> formulation
42of N-dimensional arrays). The arrays provided by the library may be
43accessed using the familiar syntax of native C++ arrays. Additional
44features, such as resizing, reshaping, and creating views are
45available (and described below).
46
47
48<h2>Table of Contents</h2>
49
50<ol>
51 <li><a href="#sec_introduction">Introduction</a>
52
53 <li><a href="#sec_example">Short Example</a>
54
55 <li><a href="#sec_components">MultiArray Components</a>
56
57 <li><a href="#sec_assignment">Construction and Assignment</a>
58
59 <li><a href="#sec_generators">Array View and Subarray Type Generators</a>
60
61 <li><a href="#sec_dimensions">Specifying Array Dimensions</a>
62
63 <li><a href="#sec_access">Accessing Elements</a>
64
65 <li><a href="#sec_views">Creating Views</a>
66
67 <li><a href="#sec_storage">Storage Ordering</a>
68
69 <li><a href="#sec_base">Setting the Array Base</a>
70
71 <li><a href="#sec_reshape">Changing an Array's Shape</a>
72
73 <li><a href="#sec_resize">Resizing an Array</a>
74
75 <li><a href="#sec_concepts">MultiArray Concept</a>
76
77 <li><a href="#sec_testcases">Test Cases</a>
78
79 <li><a href="#sec_related">Related Work</a>
80 <li><a href="#sec_credits">Credits</a>
81</ol>
82
83
84<a name="sec_introduction"></a>
85<h2>Introduction</h2>
86
87<p>
88The C++ standard library provides several generic containers, but it
89does not provide any multidimensional array types. The
90<tt>std::vector</tt> class template can be used to implement
91N-dimensional arrays, for example expressing a 2-dimensional array of
92<tt>double</tt> elements using the type
93<tt>std::vector&lt;std::vector&lt;double&gt;&gt;</tt>, but the
94resulting interface is unwieldy and the memory overhead can be quite
95high. Native C++ arrays (i.e. <tt>int arr[2][2][2];</tt>) do not
96immediately interoperate well with the C++ Standard Library, and they
97also lose information at function call boundaries (specifically the
98extent of the last dimension). Finally, a dynamically allocated
99contiguous block of elements can be treated as an array, though this
100method requires manual bookkeeping that is error prone and obfuscates
101the intent of the programmer.
102</p>
103
104<p>
105The Boost MultiArray library enhances the C++ standard containers with
106versatile multi-dimensional array abstractions. It includes a general
107array class template and native array adaptors that support idiomatic
108array operations and interoperate with C++ Standard Library containers
109and algorithms. The arrays share a common interface, expressed as a
110generic programming in terms of which generic array algorithms can be
111implemented.
112</p>
113
114<p>
115This document is meant to provide an introductory tutorial and user's
116guide for the most basic and common usage patterns of MultiArray
117components. The <a href="./reference.html">reference manual</a>
118provides more complete and formal documentation of library features.
119</p>
120
121<a name="sec_example"></a>
122<h2>Short Example</h2>
123What follows is a brief example of the use of <tt>multi_array</tt>:
124
125<blockquote>
126<pre>
127#include "boost/multi_array.hpp"
128#include &lt;cassert&gt;
129
130int
131main () {
132 // Create a 3D array that is 3 x 4 x 2
133 typedef boost::multi_array&lt;double, 3&gt; array_type;
134 typedef array_type::index index;
135 array_type A(boost::extents[3][4][2]);
136
137 // Assign values to the elements
138 int values = 0;
139 for(index i = 0; i != 3; ++i)
140 for(index j = 0; j != 4; ++j)
141 for(index k = 0; k != 2; ++k)
142 A[i][j][k] = values++;
143
144 // Verify values
145 int verify = 0;
146 for(index i = 0; i != 3; ++i)
147 for(index j = 0; j != 4; ++j)
148 for(index k = 0; k != 2; ++k)
149 assert(A[i][j][k] == verify++);
150
151 return 0;
152}
153</pre>
154</blockquote>
155
156<a name="sec_components"></a>
157<h2>MultiArray Components</h2>
158
159Boost.MultiArray's implementation (boost/multi_array.hpp) provides three user-level class templates:
160
161<ol>
162 <li><a href="./reference.html#multi_array"><tt>multi_array</tt></a>,
163
164 <li><a href="./reference.html#multi_array_ref"><tt>multi_array_ref</tt></a>, and
165
166 <li><a href="./reference.html#const_multi_array_ref"><tt>const_multi_array_ref</tt></a>
167</ol>
168
169<tt>multi_array</tt> is a container template. When instantiated, it
170allocates space for the number of elements corresponding to the
171dimensions specified at construction time. A <tt>multi_array</tt> may
172also be default constructed and resized as needed.
173
174<p>
175<tt>multi_array_ref</tt> adapts an existing array of data to provide
176the <tt>multi_array</tt> interface. <tt>multi_array_ref</tt> does not own the
177data passed to it.
178
179<p>
180<tt>const_multi_array_ref</tt> is similar to <tt>multi_array_ref</tt>
181but guarantees that the contents of the array are immutable. It can
182thus wrap pointers of type <i>T const*</i>.
183
184<p>
185The three components exhibit very similar behavior. Aside from
186constructor parameters, <tt>multi_array</tt> and
187<tt>multi_array_ref</tt> export the same interface.
188<tt>const_multi_array_ref</tt> provides only the constness-preserving
189portions of the <tt>multi_array_ref</tt> interface.
190
191<a name="sec_assignment"></a>
192<h2>Construction and Assignment</h2>
193<p>Each of the array types -
194<a href="./reference.html#multi_array"><tt>multi_array</tt></a>,
195<a href="./reference.html#multi_array_ref"><tt>multi_array_ref</tt></a>, and
196<a href="./reference.html#const_multi_array_ref"><tt>const_multi_array_ref</tt></a> -
197provides a specialized set of constructors. For further information,
198consult their reference pages.
199
200<p>All of the non-const array types in this library provide assignment
201operators<tt>operator=()</tt>. Each of the array types <tt>multi_array</tt>,
202 <tt>multi_array_ref</tt>, <tt>subarray</tt>, and
203 <tt>array_view</tt> can be assigned from any
204of the others, so long as their shapes match. The
205 const variants, <tt>const_multi_array_ref</tt>,
206<tt>const_subarray</tt>, and <tt>const_array_view</tt>, can be the
207 source of a copy to an array with matching shape.
208Assignment results in a deep (element by element) copy of the data
209contained within an array.
210
211<a name="sec_generators"></a>
212<h2>Array View and Subarray Type Generators</h2>
213In some situations, the use of nested generators for array_view and
214subarray types is inconvenient. For example, inside a
215function template parameterized upon array type, the extra
216"template" keywords can be obfuscating. More likely though, some
217compilers cannot handle templates nested within template parameters.
218For this reason the type generators, <tt>subarray_gen</tt>,
219<tt>const_subarray_gen</tt>, <tt>array_view_gen</tt>, and
220<tt>const_array_view_gen</tt> are provided. Thus, the two typedefs
221in the following example result in the same type:
222<blockquote>
223<pre>
224template &lt;typename Array&gt;
225void my_function() {
226 typedef typename Array::template array_view&lt;3&gt;::type view1_t;
227 typedef typename boost::array_view_gen&lt;Array,3&gt;::type view2_t;
228 // ...
229}
230</pre>
231</blockquote>
232
233<a name="sec_dimensions"></a>
234<h2>Specifying Array Dimensions</h2>
235When creating most of the Boost.MultiArray components, it is necessary
236to specify both the number of dimensions and the extent of each
237(<tt>boost::multi_array</tt> also provides a default constructor).
238Though the number of dimensions is always specified as a template
239parameter, two separate mechanisms have been provided to specify the
240extent of each.
241<p>The first method involves passing a
242<a href="../../utility/Collection.html">
243Collection</a> of extents to a
244constructor, most commonly a <tt>boost::array</tt>. The constructor
245will retrieve the beginning iterator from the container and retrieve N
246elements, corresponding to extents for the N dimensions. This is
247useful for writing dimension-independent code.
248<h3>Example</h3>
249<blockquote>
250<pre>
251 typedef boost::multi_array&lt;double, 3&gt; array_type;
252 boost::array&lt;array_type::index, 3&gt; shape = {{ 3, 4, 2 }};
253 array_type A(shape);
254</pre>
255</blockquote>
256
257<p>The second method involves passing the constructor an <tt>extent_gen</tt>
258object, specifying the matrix dimensions. The <tt>extent_gen</tt> type
259 is defined in the <tt>multi_array_types</tt> namespace and as a
260 member of every array type, but by default, the library constructs a
261global <tt>extent_gen</tt> object <tt>boost::extents</tt>. In case of
262concern about memory used by these objects, defining
263<tt>BOOST_MULTI_ARRAY_NO_GENERATORS</tt> before including the library
264header inhibits its construction.
265
266<h3>Example</h3>
267<blockquote>
268<pre>
269 typedef boost::multi_array&lt;double, 3&gt; array_type;
270 array_type A(boost::extents[3][4][2]);
271</pre>
272</blockquote>
273
274<a name="sec_access"></a>
275<h2>Accessing Elements</h2>
276The Boost.MultiArray components provide two ways of accessing
277specific elements within a container. The first uses the traditional
278C array notation, provided by <tt>operator[]</tt>.
279<h3>Example</h3>
280<blockquote>
281<pre>
282 typedef boost::multi_array&lt;double, 3&gt; array_type;
283 array_type A(boost::extents[3][4][2]);
284 A[0][0][0] = 3.14;
285 assert(A[0][0][0] == 3.14);
286</pre>
287</blockquote>
288
289<p> The second method involves passing a
290<a href="../../utility/Collection.html">
291Collection</a> of indices to <tt>operator()</tt>. N indices will be retrieved
292from the Collection for the N dimensions of the container.
293<h3>Example</h3>
294<blockquote>
295<pre>
296 typedef boost::multi_array&lt;double, 3&gt; array_type;
297 array_type A(boost::extents[3][4][2]);
298 boost::array&lt;array_type::index,3&gt; idx = {{0,0,0}};
299 A(idx) = 3.14;
300 assert(A(idx) == 3.14);
301</pre>
302</blockquote>
303This can be useful for writing dimension-independent code, and under
304some compilers may yield higher performance than <tt>operator[].</tt>
305
306<p>
307By default, both of the above element access methods perform range
308checking. If a supplied index is out of the range defined for an
309array, an assertion will abort the program. To disable range
310checking (for performance reasons in production releases), define
311the <tt>BOOST_DISABLE_ASSERTS</tt> preprocessor macro prior to
312including multi_array.hpp in your application.
313
314<a name="sec_views"></a>
315<h2>Creating Views</h2>
316Boost.MultiArray provides the facilities for creating a sub-view of an
317already existing array component. It allows you to create a sub-view that
318retains the same number of dimensions as the original array or one
319that has less dimensions than the original as well.
320
321<p>Sub-view creation occurs by placing a call to operator[], passing
322it an <tt>index_gen</tt> type. The <tt>index_gen</tt> is populated by
323passing <tt>index_range</tt> objects to its <tt>operator[]</tt>.
324The <tt>index_range</tt> and <tt>index_gen</tt> types are defined in
325the <tt>multi_array_types</tt> namespace and as nested members of
326every array type. Similar to <tt>boost::extents</tt>, the library by
327default constructs the object <tt>boost::indices</tt>. You can
328suppress this object by
329defining <tt>BOOST_MULTI_ARRAY_NO_GENERATORS</tt> before including the
330library header. A simple sub-view creation example follows.
331
332<h3>Example</h3>
333<blockquote>
334<pre>
335 // myarray = 2 x 3 x 4
336
337 //
338 // array_view dims: [base,bound) (dimension striding default = 1)
339 // dim 0: [0,2)
340 // dim 1: [1,3)
341 // dim 2: [0,4) (strided by 2),
342 //
343
344 typedef boost::multi_array_types::index_range range;
345 // OR typedef array_type::index_range range;
346 array_type::array_view&lt;3&gt;::type myview =
347 myarray[ boost::indices[range(0,2)][range(1,3)][range(0,4,2)] ];
348
349 for (array_type::index i = 0; i != 2; ++i)
350 for (array_type::index j = 0; j != 2; ++j)
351 for (array_type::index k = 0; k != 2; ++k)
352 assert(myview[i][j][k] == myarray[i][j+1][k*2]);
353</pre>
354</blockquote>
355
356
357<p>By passing an integral value to the index_gen, one may create a
358subview with fewer dimensions than the original array component (also
359called slicing).
360<h3>Example</h3>
361<blockquote>
362<pre>
363 // myarray = 2 x 3 x 4
364
365 //
366 // array_view dims:
367 // [base,stride,bound)
368 // [0,1,2), 1, [0,2,4)
369 //
370
371 typedef boost::multi_array_types::index_range range;
372 array_type::index_gen indices;
373 array_type::array_view&lt;2&gt;::type myview =
374 myarray[ indices[range(0,2)][1][range(0,4,2)] ];
375
376 for (array_type::index i = 0; i != 2; ++i)
377 for (array_type::index j = 0; j != 2; ++j)
378 assert(myview[i][j] == myarray[i][1][j*2]);
379</pre>
380</blockquote>
381
382<h3>More on <tt>index_range</tt></h3>
383The <tt>index_range</tt> type provides several methods of specifying
384ranges for subview generation. Here are a few range instantiations
385that specify the same range.
386<h3>Example</h3>
387<blockquote>
388<pre>
389 // [base,stride,bound)
390 // [0,2,4)
391
392 typedef boost::multi_array_types::index_range range;
393 range a_range;
394 a_range = range(0,4,2);
395 a_range = range().start(0).finish(4).stride(2);
396 a_range = range().start(0).stride(2).finish(4);
397 a_range = 0 &lt;= range().stride(2) &lt; 4;
398 a_range = 0 &lt;= range().stride(2) &lt;= 3;
399</pre>
400</blockquote>
401
402An <tt>index_range</tt> object passed to a slicing operation will
403inherit its start and/or finish value from the array being sliced if
404you do not supply one. This conveniently prevents you from having to
405know the bounds of the array dimension in certain cases. For example,
406the default-constructed range will take the full extent of the
407dimension it is used to specify.
408
409<h3>Example</h3>
410<blockquote>
411<pre>
412 typedef boost::multi_array_types::index_range range;
413 range a_range;
414
415 // All elements in this dimension
416 a_range = range();
417
418 // indices i where 3 &lt;= i
419 a_range = range().start(3)
420 a_range = 3 &lt;= range();
421 a_range = 2 &lt; range();
422
423 // indices i where i &lt; 7
424 a_range = range().finish(7)
425 a_range = range() &lt; 7;
426 a_range = range() &lt;= 6;
427</pre>
428</blockquote>
429
430The following example slicing operations exhibit some of the
431alternatives shown above
432<blockquote>
433<pre>
434 // take all of dimension 1
435 // take i &lt; 5 for dimension 2
436 // take 4 &lt;= j &lt;= 7 for dimension 3 with stride 2
437 myarray[ boost::indices[range()][range() &lt; 5 ][4 &lt;= range().stride(2) &lt;= 7] ];
438</pre>
439</blockquote>
440
441<a name="sec_storage"></a>
442<h2>Storage Ordering</h2>
443Each array class provides constructors that accept a storage ordering
444parameter. This is most
445useful when interfacing with legacy codes that require an ordering
446different from standard C, such as FORTRAN. The possibilities are
447<tt>c_storage_order</tt>, <tt>fortran_storage_order</tt>, and
448<tt>general_storage_order</tt>.
449
450<p><tt>c_storage_order</tt>, which is the default, will store elements
451in memory in the same order as a C array would, that is, the
452dimensions are stored from last to first.
453
454<p><tt>fortran_storage_order</tt> will store elements in memory in the same order
455as FORTRAN would: from the first dimension to
456the last. Note that with use of this parameter, the array
457indices will remain zero-based.
458<h3>Example</h3>
459<blockquote>
460<pre>
461 typedef boost::multi_array&lt;double,3&gt; array_type;
462 array_type A(boost::extents[3][4][2],boost::fortran_storage_order());
463 call_fortran_function(A.data());
464</pre>
465</blockquote>
466
467<p><tt>general_storage_order</tt> allows one to customize both the order in
468which dimensions are stored in memory and whether dimensions are
469stored in ascending or descending order.
470<h3>Example</h3>
471<blockquote>
472<pre>
473 typedef boost::general_storage_order&lt;3&gt; storage;
474 typedef boost::multi_array&lt;int,3&gt; array_type;
475
476 // Store last dimension, then first, then middle
477 array_type::size_type ordering[] = {2,0,1};
478
479 // Store the first dimension(dimension 0) in descending order
480 bool ascending[] = {false,true,true};
481
482 array_type A(extents[3][4][2],storage(ordering,ascending));
483</pre>
484</blockquote>
485
486
487<a name="sec_base"></a>
488<h2>Setting The Array Base</h2>
489In some situations, it may be inconvenient or awkward to use an
490array that is zero-based.
491the Boost.MultiArray components provide two facilities for changing the
492bases of an array. One may specify a pair of range values, with
493the <tt>extent_range</tt> type, to
494the <tt>extent_gen</tt> constructor in order to set the base value.
495
496<h3>Example</h3>
497<blockquote>
498<pre>
499 typedef boost::multi_array&lt;double, 3&gt; array_type;
500 typedef boost::multi_array_types::extent_range range;
501 // OR typedef array_type::extent_range range;
502
503 array_type::extent_gen extents;
504
505 // dimension 0: 0-based
506 // dimension 1: 1-based
507 // dimension 2: -1 - based
508 array_type A(extents[2][range(1,4)][range(-1,3)]);
509</pre>
510</blockquote>
511
512<p>
513An alternative is to first construct the array normally then
514reset the bases. To set all bases to the same value, use the
515<tt>reindex</tt> member function, passing it a single new index value.
516<h3>Example</h3>
517<blockquote>
518<pre>
519 typedef boost::multi_array&lt;double, 3&gt; array_type;
520
521 array_type::extent_gen extents;
522
523 array_type A(extents[2][3][4]);
524 // change to 1-based
525 A.reindex(1)
526</pre>
527</blockquote>
528
529<p>
530An alternative is to set each base separately using the
531<tt>reindex</tt> member function, passing it a Collection of index bases.
532<h3>Example</h3>
533<blockquote>
534<pre>
535 typedef boost::multi_array&lt;double, 3&gt; array_type;
536
537 array_type::extent_gen extents;
538
539 // dimension 0: 0-based
540 // dimension 1: 1-based
541 // dimension 2: (-1)-based
542 array_type A(extents[2][3][4]);
543 boost::array&lt;array_type::index,ndims&gt; bases = {{0, 1, -1}};
544 A.reindex(bases);
545</pre>
546</blockquote>
547
548
549<a name="sec_reshape"></a>
550<h2>Changing an Array's Shape</h2>
551The Boost.MultiArray arrays provide a reshape operation. While the
552number of dimensions must remain the same, the shape of the array may
553change so long as the total number of
554elements contained remains the same.
555<h3>Example</h3>
556<blockquote>
557<pre>
558 typedef boost::multi_array&lt;double, 3&gt; array_type;
559
560 array_type::extent_gen extents;
561 array_type A(extents[2][3][4]);
562 boost::array&lt;array_type::index,ndims&gt; dims = {{4, 3, 2}};
563 A.reshape(dims);
564</pre>
565</blockquote>
566
567<p>
568Note that reshaping an array does not affect the indexing.
569
570<a name="sec_resize"></a>
571<h2>Resizing an Array</h2>
572
573The <tt>boost::multi_array</tt> class provides an element-preserving
574resize operation. The number of dimensions must remain the same, but
575the extent of each dimension may be increased and decreased as
576desired. When an array is made strictly larger, the existing elements
577will be preserved by copying them into the new underlying memory and
578subsequently destructing the elements in the old underlying memory.
579Any new elements in the array are default constructed. However, if
580the new array size shrinks some of the dimensions, some elements will
581no longer be available.
582
583<h3>Example</h3>
584<blockquote>
585<pre>
586 typedef boost::multi_array&lt;int, 3&gt; array_type;
587
588 array_type::extent_gen extents;
589 array_type A(extents[3][3][3]);
590 A[0][0][0] = 4;
591 A[2][2][2] = 5;
592 A.resize(extents[2][3][4]);
593 assert(A[0][0][0] == 4);
594 // A[2][2][2] is no longer valid.
595</pre>
596</blockquote>
597
598
599<a name="sec_concepts"></a>
600<h2>MultiArray Concept</h2>
601Boost.MultiArray defines and uses the
602<a href="./reference.html#MultiArray">MultiArray</a>
603concept. It specifies an interface for N-dimensional containers.
604
605<a name="sec_testcases"></a>
606<h2>Test Cases</h2>
607Boost.MultiArray comes with a suite of test cases meant to exercise
608the features and semantics of the library. A description of the test
609cases can be found <a href="./test_cases.html">here</a>.
610
611<a name="sec_related"></a>
612<h2>Related Work</h2>
613
614<a href="../../array/index.html">boost::array</a>
615 and <a href="http://www.sgi.com/tech/stl/Vector.html">std::vector</a> are
616 one-dimensional containers of user data. Both manage their own
617 memory. <tt>std::valarray</tt> is a low-level
618 C++ Standard Library component
619 meant to provide portable high performance for numerical applications.
620<a href="http://www.oonumerics.org/blitz/">Blitz++</a> is
621 an array library developed by Todd
622 Veldhuizen. It uses
623 advanced C++ techniques to provide near-Fortran performance for
624 array-based numerical applications.
625 <b>array_traits</b> is a beta library, formerly distributed with
626 Boost, that provides a means to create iterators over native C++
627 arrays.
628
629This library is analogous to
630<a href="../../array/index.html">boost::array</a> in that it augments C style N-dimensional
631arrays, as <tt>boost::array</tt> does for C one-dimensional arrays.
632
633
634<a name="sec_credits"></a>
635<h2>Credits</h2>
636<ul>
637
638 <li><a href="mailto:garcia@osl.iu.edu">Ronald Garcia</a>
639 is the primary author of the library.
640
641 <li><a href="http://www.boost.org/people/jeremy_siek.htm">Jeremy Siek</a>
642 helped with the library and provided a sounding board for ideas,
643 advice, and assistance porting to Microsoft Visual C++.
644
645 <li><a href="mailto:gbavestrelli@yahoo.com">Giovanni Bavestrelli</a>
646 provided an early implementation of an
647 N-dimensional array which inspired feedback from the
648 <a href="http://www.boost.org/">Boost</a> mailing list
649 members. Some design decisions in this work were based upon this
650 implementation and the comments it elicited.
651
652 <li><a href="mailto:tveldhui@acm.org">Todd Veldhuizen</a> wrote
653 <a href="http://oonumerics.org/blitz/">Blitz++</a>, which
654 inspired some aspects of this design. In addition, he supplied
655 feedback on the design and implementation of the library.
656
657 <li><a href="mailto:jewillco@osl.iu.edu">Jeremiah Willcock</a>
658 provided feedback on the implementation and design of the
659 library and some suggestions for features.
660
661 <li><a href="mailto:bdawes@acm.org">Beman Dawes</a>
662 helped immensely with porting the library to Microsoft Windows
663 compilers.
664</ul>
665
666<hr>
667
668<address>
669<a href="mailto:garcia@.cs.indiana.edu">Ronald Garcia</a>
670</address>
671<!-- Created: Fri Jun 29 10:53:07 EST 2001 -->
672<!-- hhmts start -->Last modified: Tue Feb 7 17:15:50 EST 2006 <!-- hhmts end -->
673
674</body>
675</html>