Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/libs/multi_array/doc/user.html @ 29

Last change on this file since 29 was 29, checked in by landauf, 17 years ago

updated boost from 1_33_1 to 1_34_1

File size: 22.4 KB
Line 
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. By default, the library constructs a
259global <tt>extent_gen</tt> object <tt>boost::extents</tt>.  In case of
260concern about memory used by these objects, defining
261<tt>BOOST_MULTI_ARRAY_NO_GENERATORS</tt> before including the library
262header inhibits its construction.
263
264<h3>Example</h3>
265<blockquote>
266<pre>
267  typedef boost::multi_array&lt;double, 3&gt; array_type;
268  array_type A(boost::extents[3][4][2]);
269</pre>
270</blockquote>
271
272<a name="sec_access"></a>
273<h2>Accessing Elements</h2>
274The Boost.MultiArray components provide two ways of accessing
275specific elements within a container.  The first uses the traditional
276C array notation, provided by <tt>operator[]</tt>.
277<h3>Example</h3>
278<blockquote>
279<pre>
280  typedef boost::multi_array&lt;double, 3&gt; array_type;
281  array_type A(boost::extents[3][4][2]);
282  A[0][0][0] = 3.14;
283  assert(A[0][0][0] == 3.14);
284</pre>
285</blockquote>
286
287<p> The second method involves passing a
288<a href="../../utility/Collection.html">
289Collection</a> of indices to <tt>operator()</tt>.  N indices will be retrieved
290from the Collection for the N dimensions of the container.
291<h3>Example</h3>
292<blockquote>
293<pre>
294  typedef boost::multi_array&lt;double, 3&gt; array_type;
295  array_type A(boost::extents[3][4][2]);
296  boost::array&lt;array_type::index,3&gt; idx = {{0,0,0}};
297  A(idx) = 3.14;
298  assert(A(idx) == 3.14);
299</pre>
300</blockquote>
301This can be useful for writing dimension-independent code, and under
302some compilers may yield higher performance than <tt>operator[].</tt>
303
304<p>
305By default, both of the above element access methods perform range
306checking.  If a supplied index is out of the range defined for an
307array, an assertion will abort the program.  To disable range
308checking (for performance reasons in production releases), define
309the <tt>BOOST_DISABLE_ASSERTS</tt> preprocessor macro prior to
310including multi_array.hpp in your application.
311
312<a name="sec_views"></a>
313<h2>Creating Views</h2>
314Boost.MultiArray provides the facilities for creating a sub-view of an
315already existing array component.  It allows you to create a sub-view that
316retains the same number of dimensions as the original array or one
317that has less dimensions than the original as well.
318
319<p>Sub-view creation occurs by placing a call to operator[], passing it
320an <tt>index_gen</tt> type.  The <tt>index_gen</tt> is populated by
321passing <tt>index_range</tt> objects to its <tt>operator[]</tt>.
322Similar to <tt>boost::extents</tt>, the library by default constructs
323the object <tt>boost::indices</tt>.  You can suppress this object
324by defining <tt>BOOST_MULTI_ARRAY_NO_GENERATORS</tt> before
325including the library header. A simple sub-view creation example follows.
326<h3>Example</h3>
327<blockquote>
328<pre>
329  // myarray = 2 x 3 x 4
330
331  //
332  // array_view dims: [base,bound) (dimension striding default = 1)
333  // dim 0: [0,2)
334  // dim 1: [1,3)
335  // dim 2: [0,4) (strided by 2),
336  //
337
338  typedef array_type::index_range range;
339  array_type::array_view&lt;3&gt;::type myview =
340    myarray[ boost::indices[range(0,2)][range(1,3)][range(0,4,2)] ];
341
342  for (array_type::index i = 0; i != 2; ++i)
343    for (array_type::index j = 0; j != 2; ++j)
344      for (array_type::index k = 0; k != 2; ++k)
345        assert(myview[i][j][k] == myarray[i][j+1][k*2]);
346</pre>
347</blockquote>
348
349
350<p>By passing an integral value to the index_gen, one may create a
351subview with fewer dimensions than the original array component (also
352called slicing).
353<h3>Example</h3>
354<blockquote>
355<pre>
356  // myarray = 2 x 3 x 4
357
358  //
359  // array_view dims:
360  // [base,stride,bound)
361  // [0,1,2), 1, [0,2,4)
362  //
363
364  typedef array_type::index_range range;
365  array_type::index_gen indices;
366  array_type::array_view&lt;2&gt;::type myview =
367    myarray[ indices[range(0,2)][1][range(0,4,2)] ];
368
369  for (array_type::index i = 0; i != 2; ++i)
370    for (array_type::index j = 0; j != 2; ++j)
371        assert(myview[i][j] == myarray[i][1][j*2]);
372</pre>
373</blockquote>
374
375<h3>More on <tt>index_range</tt></h3>
376The <tt>index_range</tt> type provides several methods of specifying
377ranges for subview generation.  Here are a few range instantiations
378that specify the same range.
379<h3>Example</h3>
380<blockquote>
381<pre>
382  // [base,stride,bound)
383  // [0,2,4)
384
385  typedef array_type::index_range range;
386  range a_range;
387  a_range = range(0,4,2);
388  a_range = range().start(0).finish(4).stride(2);
389  a_range = range().start(0).stride(2).finish(4);
390  a_range = 0 &lt;= range().stride(2) &lt; 4;
391  a_range = 0 &lt;= range().stride(2) &lt;= 3;
392</pre>
393</blockquote>
394
395An <tt>index_range</tt> object passed to a slicing operation will
396inherit its start and/or finish value from the array being sliced if
397you do not supply one.  This conveniently prevents you from having to
398know the bounds of the array dimension in certain cases. For example,
399the default-constructed range will take the full extent of the
400dimension it is used to specify.
401
402<h3>Example</h3>
403<blockquote>
404<pre>
405  typedef array_type::index_range range;
406  range a_range;
407
408  // All elements in this dimension
409  a_range = range();
410
411  // indices i where 3 &lt;= i
412  a_range = range().start(3)
413  a_range = 3 &lt;= range();
414  a_range = 2 &lt; range();
415
416  // indices i where i &lt; 7
417  a_range = range().finish(7)
418  a_range = range() &lt; 7;
419  a_range = range() &lt;= 6;
420</pre>
421</blockquote>
422
423The following example slicing operations exhibit some of the
424alternatives shown above
425<blockquote>
426<pre>
427    // take all of dimension 1
428    // take i &lt; 5 for dimension 2
429    // take 4 &lt;= j &lt;= 7 for dimension 3 with stride 2
430    myarray[ boost::indices[range()][range() &lt; 5 ][4 &lt;= range().stride(2) &lt;= 7] ];
431</pre>
432</blockquote>
433
434<a name="sec_storage"></a>
435<h2>Storage Ordering</h2>
436Each array class provides constructors that accept a storage ordering
437parameter. This is most
438useful when interfacing with legacy codes that require an ordering
439different from standard C, such as FORTRAN. The possibilities are
440<tt>c_storage_order</tt>, <tt>fortran_storage_order</tt>, and
441<tt>general_storage_order</tt>.
442
443<p><tt>c_storage_order</tt>, which is the default, will store elements
444in memory in the same order as a C array would, that is, the
445dimensions are stored from last to first.
446
447<p><tt>fortran_storage_order</tt> will store elements in memory in the same order
448as FORTRAN would: from the first dimension to
449the last. Note that with use of this parameter, the array
450indices will remain zero-based.
451<h3>Example</h3>
452<blockquote>
453<pre>
454  typedef boost::multi_array&lt;double,3&gt; array_type;
455  array_type A(boost::extents[3][4][2],boost::fortran_storage_order);
456  call_fortran_function(A.data());
457</pre>
458</blockquote>
459
460<p><tt>general_storage_order</tt> allows one to customize both the order in
461which dimensions are stored in memory and whether dimensions are
462stored in ascending or descending order.
463<h3>Example</h3>
464<blockquote>
465<pre>
466  typedef boost::general_storage_order&lt;3&gt; storage;
467  typedef boost::multi_array&lt;int,3&gt; array_type;
468 
469  // Store last dimension, then first, then middle
470  array_type::size_type ordering[] = {2,0,1};
471
472  // Store the first dimension(dimension 0) in descending order
473  bool ascending[] = {false,true,true};
474
475  array_type A(extents[3][4][2],storage(ordering,ascending));
476</pre>
477</blockquote>
478
479
480<a name="sec_base"></a>
481<h2>Setting The Array Base</h2>
482In some situations, it may be inconvenient or awkward to use an
483array that is zero-based.
484the Boost.MultiArray components provide two facilities for changing the
485bases of an array.  One may specify a pair of range values to
486the extent_gen constructor in order to set the base value.
487<h3>Example</h3>
488<blockquote>
489<pre>
490  typedef boost::multi_array&lt;double, 3&gt; array_type;
491  typedef array_type::extent_range range;
492
493  array_type::extent_gen extents;
494 
495  // dimension 0: 0-based
496  // dimension 1: 1-based
497  // dimension 2: -1 - based
498  array_type A(extents[2][range(1,4)][range(-1,3)]);
499</pre>
500</blockquote>
501
502<p>
503An alternative is to first construct the array normally then
504reset the bases.  To set all bases to the same value, use the
505<tt>reindex</tt> member function, passing it a single new index value.
506<h3>Example</h3>
507<blockquote>
508<pre>
509  typedef boost::multi_array&lt;double, 3&gt; array_type;
510  typedef array_type::extent_range range;
511
512  array_type::extent_gen extents;
513 
514  array_type A(extents[2][3][4]);
515  // change to 1-based
516  A.reindex(1)
517</pre>
518</blockquote>
519
520<p>
521An alternative is to set each base separately using the
522<tt>reindex</tt> member function, passing it a Collection of index bases.
523<h3>Example</h3>
524<blockquote>
525<pre>
526  typedef boost::multi_array&lt;double, 3&gt; array_type;
527  typedef array_type::extent_range range;
528
529  array_type::extent_gen extents;
530 
531  // dimension 0: 0-based
532  // dimension 1: 1-based
533  // dimension 2: (-1)-based
534  array_type A(extents[2][3][4]);
535  boost::array&lt;array_type::index,ndims&gt; bases = {{0, 1, -1}};       
536  A.reindex(bases);
537</pre>
538</blockquote>
539
540
541<a name="sec_reshape"></a>
542<h2>Changing an Array's Shape</h2>
543The Boost.MultiArray arrays provide a reshape operation.  While the
544number of dimensions must remain the same, the shape of the array may
545change so long as the total number of
546elements contained remains the same.
547<h3>Example</h3>
548<blockquote>
549<pre>
550  typedef boost::multi_array&lt;double, 3&gt; array_type;
551  typedef array_type::extent_range range;
552
553  array_type::extent_gen extents;
554  array_type A(extents[2][3][4]);
555  boost::array&lt;array_type::index,ndims&gt; dims = {{4, 3, 2}};       
556  A.reshape(dims);
557</pre>
558</blockquote>
559
560<p>
561Note that reshaping an array does not affect the indexing.
562
563<a name="sec_resize"></a>
564<h2>Resizing an Array</h2>
565
566The <tt>boost::multi_array</tt> class provides an element-preserving
567resize operation.  The number of dimensions must remain the same, but
568the extent of each dimension may be increased and decreased as
569desired.  When an array is made strictly larger, the existing elements
570will be preserved by copying them into the new underlying memory and
571subsequently destructing the elements in the old underlying memory.
572Any new elements in the array are default constructed.  However, if
573the new array size shrinks some of the dimensions, some elements will
574no longer be available.
575
576<h3>Example</h3>
577<blockquote>
578<pre>
579  typedef boost::multi_array&lt;int, 3&gt; array_type;
580  typedef array_type::extent_range range;
581
582  array_type::extent_gen extents;
583  array_type A(extents[3][3][3]);
584  A[0][0][0] = 4;
585  A[2][2][2] = 5;
586  A.resize(extents[2][3][4]);
587  assert(A[0][0][0] == 4);
588  // A[2][2][2] is no longer valid.
589</pre>
590</blockquote>
591
592
593<a name="sec_concepts"></a>
594<h2>MultiArray Concept</h2>
595Boost.MultiArray defines and uses the
596<a href="./reference.html#MultiArray">MultiArray</a>
597concept.  It specifies an interface for N-dimensional containers.
598
599<a name="sec_testcases"></a>
600<h2>Test Cases</h2>
601Boost.MultiArray comes with a suite of test cases meant to exercise
602the features and semantics of the library.  A description of the test
603cases can be found <a href="./test_cases.html">here</a>.
604
605<a name="sec_related"></a>
606<h2>Related Work</h2>
607
608<a href="../../array/index.html">boost::array</a>
609 and <a href="http://www.sgi.com/tech/stl/Vector.html">std::vector</a> are
610    one-dimensional containers of user data.  Both manage their own
611    memory. <tt>std::valarray</tt> is a low-level
612    C++ Standard Library component
613    meant to provide portable high performance for numerical applications.
614<a href="http://www.oonumerics.org/blitz/">Blitz++</a> is
615    an array library developed by Todd
616    Veldhuizen. It uses
617    advanced C++ techniques to provide near-Fortran performance for
618    array-based numerical applications.
619    <b>array_traits</b> is a beta library, formerly distributed with
620    Boost, that provides a means to create iterators over native C++
621    arrays.
622
623This library is analogous to
624<a href="../../array/index.html">boost::array</a> in that it augments C style N-dimensional
625arrays, as <tt>boost::array</tt> does for C one-dimensional arrays.
626
627
628<a name="sec_credits"></a>
629<h2>Credits</h2>
630<ul>
631     
632      <li><a href="mailto:garcia@osl.iu.edu">Ronald Garcia</a> 
633      is the primary author of the library.
634
635      <li><a href="../../../people/jeremy_siek.htm">Jeremy Siek</a>
636      helped with the library and provided a sounding board for ideas,
637       advice, and assistance porting to Microsoft Visual C++.
638
639      <li><a href="mailto:gbavestrelli@yahoo.com">Giovanni Bavestrelli</a>
640      provided an early implementation of an
641      N-dimensional array which inspired feedback from the
642      <a href="http://www.boost.org/">Boost</a> mailing list
643      members. Some design decisions in this work were based upon this
644      implementation and the comments it elicited.
645
646      <li><a href="mailto:tveldhui@acm.org">Todd Veldhuizen</a> wrote
647      <a href="http://oonumerics.org/blitz/">Blitz++</a>, which
648      inspired some aspects of this design. In addition, he supplied
649      feedback on the design and implementation of the library.
650
651      <li><a href="mailto:jewillco@osl.iu.edu">Jeremiah Willcock</a>
652      provided feedback on the implementation and design of the
653      library and some suggestions for features.
654
655      <li><a href="mailto:bdawes@acm.org">Beman Dawes</a>
656      helped immensely with porting the library to Microsoft Windows
657      compilers.
658</ul>
659
660<hr>
661
662<address>
663<a href="mailto:garcia@.cs.indiana.edu">Ronald Garcia</a>
664</address>
665<!-- Created: Fri Jun 29 10:53:07 EST 2001 -->
666<!-- hhmts start -->Last modified: Tue Feb  7 17:15:50 EST 2006 <!-- hhmts end -->
667
668</body>
669</html>
Note: See TracBrowser for help on using the repository browser.